WebKit Bugzilla
Attachment 340989 Details for
Bug 185003
: We should have a CoW storage for NewArrayBuffer arrays.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for landing
bug-185003-20180522094839.patch (text/plain), 259.13 KB, created by
Keith Miller
on 2018-05-22 09:48:41 PDT
(
hide
)
Description:
Patch for landing
Filename:
MIME Type:
Creator:
Keith Miller
Created:
2018-05-22 09:48:41 PDT
Size:
259.13 KB
patch
obsolete
>Subversion Revision: 231997 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 88cdd95b5162a2c12027f68bfed82e5557792be1..8975222069d22997d34fa6c49314bd94faabc080 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -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-18 Commit Queue <commit-queue@webkit.org> > > Unreviewed, rolling out r231982. >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 3e0e2b5a271fdcc2d70101d315913a5eeea34c6a..25f303ed459c96ef314089aa9eddaff4ac2d8476 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -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-19 Commit Queue <commit-queue@webkit.org> > > Unreviewed, rolling out r231996. >diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >index 9e7f7bf7612da93cf227681a4296295787ae2d0e..79df1cab5df583c77ce795988b70537ebc8023ee 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >@@ -1032,6 +1032,7 @@ > 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 */; }; >@@ -3295,6 +3296,7 @@ > 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>"; }; >@@ -3458,6 +3460,8 @@ > 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>"; }; >@@ -5859,9 +5863,9 @@ > 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 */, >@@ -6177,6 +6181,7 @@ > 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 */, >@@ -6669,6 +6674,7 @@ > 865A30F0135007E100CDB49E /* JSCJSValueInlines.h */, > FE2B0B681FD0D2970075DA5F /* JSCPoison.cpp */, > FE2B0B701FD8C4630075DA5F /* JSCPoison.h */, >+ FE7497E5209001B00003565B /* JSCPtrTag.h */, > 72AAF7CB1D0D318B005E60BE /* JSCustomGetterSetterFunction.cpp */, > 72AAF7CC1D0D318B005E60BE /* JSCustomGetterSetterFunction.h */, > 0F2B66BD17B6B5AB00A7AE3F /* JSDataView.cpp */, >@@ -6706,6 +6712,8 @@ > BC756FC60E2031B200DE7D12 /* JSGlobalObjectFunctions.cpp */, > BC756FC70E2031B200DE7D12 /* JSGlobalObjectFunctions.h */, > 79B819921DD25CF500DDC714 /* JSGlobalObjectInlines.h */, >+ 53F11F422091749800E411A7 /* JSImmutableButterfly.cpp */, >+ 53F11F40209138D700E411A7 /* JSImmutableButterfly.h */, > 0F2B66CA17B6B5AB00A7AE3F /* JSInt16Array.h */, > 0F2B66CB17B6B5AB00A7AE3F /* JSInt32Array.h */, > 0F2B66C917B6B5AB00A7AE3F /* JSInt8Array.h */, >@@ -6754,7 +6762,6 @@ > 2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */, > 862553CE16136AA5009F17D0 /* JSProxy.cpp */, > 862553CF16136AA5009F17D0 /* JSProxy.h */, >- FE7497E5209001B00003565B /* JSCPtrTag.h */, > 534638721E70D01500F12AC1 /* JSRunLoopTimer.cpp */, > 534638701E70CF3D00F12AC1 /* JSRunLoopTimer.h */, > 14874AE115EBDE4A002E3587 /* JSScope.cpp */, >@@ -8610,7 +8617,6 @@ > 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 */, >@@ -8900,6 +8906,7 @@ > 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 */, >@@ -8948,6 +8955,7 @@ > 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 */, >@@ -9005,6 +9013,7 @@ > 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 */, >@@ -9041,6 +9050,7 @@ > 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 */, >@@ -9080,7 +9090,6 @@ > 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 */, >@@ -9123,6 +9132,7 @@ > 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 */, >@@ -9213,7 +9223,6 @@ > 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 */, >@@ -9258,7 +9267,6 @@ > 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 */, >diff --git a/Source/JavaScriptCore/Sources.txt b/Source/JavaScriptCore/Sources.txt >index df224083870086445a603dc10ce899e44af6fe52..394b5c30c13a55dff29f230d573e59863ac6fdbb 100644 >--- a/Source/JavaScriptCore/Sources.txt >+++ b/Source/JavaScriptCore/Sources.txt >@@ -804,6 +804,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 >diff --git a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp >index 41ce23c385fef66412cbce30169b363f901c137f..bd73651ab22cdc1015b67fa8b8c366aeea133b2b 100644 >--- a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp >+++ b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp >@@ -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; >diff --git a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h >index 1bfb470deeb733ea1cca90398e962bdde8933dcc..c233deb7a24114553257a9ab8fc874e1e8709a6f 100644 >--- a/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h >+++ b/Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h >@@ -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 }; >diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp >index c1c7dbd67cccbba1098e948fedff2e41a3135390..d071b0d4d39138b083860625bce530aad4ca3a9c 100644 >--- a/Source/JavaScriptCore/bytecode/ArrayProfile.cpp >+++ b/Source/JavaScriptCore/bytecode/ArrayProfile.cpp >@@ -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(); > } > >diff --git a/Source/JavaScriptCore/bytecode/ArrayProfile.h b/Source/JavaScriptCore/bytecode/ArrayProfile.h >index 73eb88291cef1b6e0d8b55f19afb97674f51e4ce..408960fde621953e8388837abc3dc3a784fe861e 100644 >--- a/Source/JavaScriptCore/bytecode/ArrayProfile.h >+++ b/Source/JavaScriptCore/bytecode/ArrayProfile.h >@@ -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&); > >diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json >index 5628df5b462163eb296321fe858d03b6d9f76e55..473abe78f5aaa7995049892b39bc5a79833ab612 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeList.json >+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json >@@ -20,9 +20,12 @@ > { "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 }, >diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >index 8f1a4bd90e3cab0b5b02cfbf8611ade257933441..1d31e75076e2ec47ee5f2d71da26428a85f680a3 100644 >--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp >+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp >@@ -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]; >diff --git a/Source/JavaScriptCore/bytecode/InlineAccess.cpp b/Source/JavaScriptCore/bytecode/InlineAccess.cpp >index 9130b1e25d44e6635817ea78a483ef8cad679790..a455f6f75014c0e40743de40057dc753253fb1f8 100644 >--- a/Source/JavaScriptCore/bytecode/InlineAccess.cpp >+++ b/Source/JavaScriptCore/bytecode/InlineAccess.cpp >@@ -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()); >diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >index 6acb500f89de33e9c053c02f4907421570947d04..5ae594b37e327d1dc07f1c09021ebf22a9d05143 100644 >--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >@@ -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; } >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index 6d6449b30e278c3efae7082ee9e7005efd0b59b6..d25d9c8a2f56ca1e50a65a76ee1782f4ce77bc78 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -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() >@@ -3177,16 +3178,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()) { >@@ -3204,7 +3205,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; > } > >@@ -3256,7 +3257,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; > } >@@ -3450,7 +3451,7 @@ ExpectedFunction BytecodeGenerator::emitExpectedFunctionSnippet(RegisterID* dst, > instructions().append(dst->index()); > instructions().append(0); > instructions().append(0); >- instructions().append(newArrayAllocationProfile()); >+ instructions().append(newArrayAllocationProfile(ArrayWithUndecided)); > } > } > break; >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >index b7255ac6216e09dce1ad4d7c669ec546a2cb4673..a31a8e2fa6b3f7d13319a967ab5107dd7f5cdf70 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -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*); >@@ -1005,7 +1007,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) >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 07a9c907a7e2130a4244dfa0011f7ca52b06e8a6..3f6150063da7d1461dfb3335df50004d427b1287 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -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) >@@ -4151,7 +4158,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) >@@ -4203,7 +4210,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; >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index 9bd8a533b1ce1d64c9323339c80f1475879f44c9..b97a9d28477871b14430cb1ab12ac1870bd19c6f 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -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: >diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp >index 3f2131487a995f34f49fe610175db9c9b6b56f1b..a3d04adf37bff8b42c91d4078c97b39687918c49 100644 >--- a/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp >@@ -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) >diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp >index d12998918c233d49700495544f78eec1f51664c2..cc75f47c3f2dbfcc69689e4581faa7d5c8526acb 100644 >--- a/Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp >+++ b/Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp >@@ -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; >diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp >index 216bbeb6b37c6e5aa5d1792cc87bfad09ec3fad1..0e88d2a36fb47d8306222bcc90441b6b305e157b 100644 >--- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp >+++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp >@@ -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; >diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h >index 81bcf82716b75d6d5c70e70d7d170a7706074faf..1a15fec20c5e51ee0bde47f121f92e950fd51169 100644 >--- a/Source/JavaScriptCore/dfg/DFGArrayMode.h >+++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h >@@ -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) >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 3874fcc5b36dd94275ab253269550c132394e3d5..1430943c2cb94cef8fdb64191d6fcf04f5362b9b 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -50,6 +50,7 @@ > #include "Heap.h" > #include "JSCInlines.h" > #include "JSFixedArray.h" >+#include "JSImmutableButterfly.h" > #include "JSModuleEnvironment.h" > #include "JSModuleNamespaceObject.h" > #include "NumberConstructor.h" >@@ -2426,6 +2427,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: >@@ -2453,6 +2455,7 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin > case AtomicsLoadIntrinsic: > op = AtomicsLoad; > numArgs = 2; >+ action = Array::Read; > break; > case AtomicsOrIntrinsic: > op = AtomicsOr; >@@ -2488,12 +2491,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); >@@ -2528,7 +2531,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; >@@ -2541,7 +2544,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; >@@ -3205,7 +3208,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); >@@ -3232,7 +3235,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; > >@@ -3250,7 +3253,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; > } >@@ -4568,18 +4571,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); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index ad623647a77040c0d3b826cdc1a370c1a23b9bdc..b9f74a2a49629b1f7439016b8b0b9c9a74d2f408 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -37,6 +37,7 @@ > #include "DOMJITSignature.h" > #include "InlineCallFrame.h" > #include "JSFixedArray.h" >+#include "JSImmutableButterfly.h" > > namespace JSC { namespace DFG { > >@@ -1420,7 +1421,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)))); >diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >index 7374185667e0bdb787a83b702cb18959b5069d01..78e1a2efb5ba1e82ea2cfe1303bdee91297b50f9 100644 >--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >@@ -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) { >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index 0101d379bcd6f745e72a88ec219fbcf329e38a9b..798fe0bed30f0238bb60d32ca5a46fdd932cb8bc 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -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; > } > >@@ -2172,13 +2172,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> >@@ -3114,7 +3114,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); >@@ -3127,7 +3127,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); > } > } > >diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp >index bb5433c0d3d32e5a02b04e9a5b83ee5310a009ff..d88eae65a29f5d81b414c6e418bf5fa3228da3cf 100644 >--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp >+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp >@@ -318,7 +318,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()) >diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h >index 4d97cd3ab127d30bbf46ac739edbf1bea85c130d..53b1ab021ec34f40dd0d71f89ba620d0497bb5e5 100644 >--- a/Source/JavaScriptCore/dfg/DFGNode.h >+++ b/Source/JavaScriptCore/dfg/DFGNode.h >@@ -100,7 +100,7 @@ struct NewArrayBufferData { > union { > struct { > unsigned vectorLengthHint; >- unsigned indexingType; >+ unsigned indexingMode; > }; > uint64_t asQuadWord; > }; >@@ -1170,7 +1170,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>()); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp >index a471bf0a38c75ca286eec3d827d146296219f33d..00f5977b473177666e9ba48517add77bb55bb1ed 100644 >--- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp >+++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp >@@ -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); >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp >index 9e7daf486390aaf0c30e580d3f1a1927fc449e09..e08d2e919731ea036f73885a00b3145de34170a9 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp >+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp >@@ -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) >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h >index 16ba1a2f25b63cdd20f4ab0cc80ca4cde2e86d32..6309ec38d8b5190a3afa6e26cae9c6775f0f455a 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.h >+++ b/Source/JavaScriptCore/dfg/DFGOperations.h >@@ -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; >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index 3f934e3b11d81c8fbbb221b767e01a340452c1ca..354a7091482e4208a17796ee7936e93d99137bb4 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -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()), >@@ -2179,7 +2187,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( >@@ -6662,7 +6670,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, >@@ -6709,7 +6717,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()); >@@ -6856,7 +6864,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, >@@ -6880,7 +6888,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); >@@ -7514,7 +7522,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); >@@ -8083,7 +8091,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); > } > > { >@@ -12021,47 +12030,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; > } >@@ -12069,7 +12058,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); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index 9f131d914b1e5002b5466c174a7878e6a67534e2..7b621d5da5d86c1a75533d3c4cb8c50eacc93b4f 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -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())); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index a2caa003afbd6ffa7bcde182e40a277fc32264aa..7b08170944cd15ad60950194ecf73b5b3a4d91d9 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -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())); >diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp >index d7726bbe1df4c2b0b72d97304bf8777cbdd1e53d..b7895e54abfd4157bd3140de70975ee5273c41d3 100644 >--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp >+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp >@@ -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: { >diff --git a/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h b/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h >index a828711ff5978ba158a626113d2991c4ca76e46e..5f450d1f2aeb1d70adcc5fa1731292eac252fc21 100644 >--- a/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h >+++ b/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h >@@ -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()) \ >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index 12c93386afa5ff33c83a7a6077500b2aba3b9267..dd573d2eaf00799c6ce036e658016bede190bb2d 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -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" >@@ -3041,7 +3042,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()); > >@@ -4713,7 +4714,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. >@@ -5485,7 +5487,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(); > } > } > >@@ -5533,7 +5535,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. >@@ -5807,39 +5809,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() >@@ -7577,7 +7572,7 @@ private: > } > > if (target->op() == PhantomNewArrayBuffer) { >- staticArgumentCount += target->castOperand<JSFixedArray*>()->length(); >+ staticArgumentCount += target->castOperand<JSImmutableButterfly*>()->length(); > return; > } > >@@ -7728,7 +7723,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 >@@ -8415,7 +8410,7 @@ private: > } > > if (target->op() == PhantomNewArrayBuffer) { >- numberOfStaticArguments += target->castOperand<JSFixedArray*>()->length(); >+ numberOfStaticArguments += target->castOperand<JSImmutableButterfly*>()->length(); > return; > } > >@@ -8460,7 +8455,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. >@@ -12588,7 +12583,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); > } > >@@ -15000,6 +14995,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); > >@@ -15010,18 +15009,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; >diff --git a/Source/JavaScriptCore/ftl/FTLOperations.cpp b/Source/JavaScriptCore/ftl/FTLOperations.cpp >index 7508bb032cf23ee927ca4b3c35fb638c46a87fa7..a77655ba24b03682560f178f9a02f47f5823384d 100644 >--- a/Source/JavaScriptCore/ftl/FTLOperations.cpp >+++ b/Source/JavaScriptCore/ftl/FTLOperations.cpp >@@ -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: { >diff --git a/Source/JavaScriptCore/generate-bytecode-files b/Source/JavaScriptCore/generate-bytecode-files >index 8300bd080fec55a186c42e572fb985efaa5d4dfb..fa25fd2ef31be4c1eb3c3a585be529d67cfed6d8 100644 >--- a/Source/JavaScriptCore/generate-bytecode-files >+++ b/Source/JavaScriptCore/generate-bytecode-files >@@ -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"]))) >diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp >index 48a911c70ab250e33b9075ff2ffdd2ec0b8462dd..bb623cac694cf95204246a3342edf9ff2d45851e 100644 >--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp >+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp >@@ -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); >diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.cpp b/Source/JavaScriptCore/jit/AssemblyHelpers.cpp >index 29ad53ed72f77b709a9f97ca34acaf67302e2a15..8529dda3fddeac621c87c6b139e69fbd68239d93 100644 >--- a/Source/JavaScriptCore/jit/AssemblyHelpers.cpp >+++ b/Source/JavaScriptCore/jit/AssemblyHelpers.cpp >@@ -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); > >diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.h b/Source/JavaScriptCore/jit/AssemblyHelpers.h >index 56251991ac91154d69c77f14baf6e9fc3bb7d091..f71e74030638efea2014fb42b0647edcc0e82a53 100644 >--- a/Source/JavaScriptCore/jit/AssemblyHelpers.h >+++ b/Source/JavaScriptCore/jit/AssemblyHelpers.h >@@ -1465,7 +1465,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 >diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp >index bf1677e22c73e24641cbe8e0e39e55064097be11..4e730b6c9182c407b6dac7a24a1c4717dad1fd88 100644 >--- a/Source/JavaScriptCore/jit/JITOperations.cpp >+++ b/Source/JavaScriptCore/jit/JITOperations.cpp >@@ -662,6 +662,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; >@@ -730,6 +731,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); > >diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >index 7e5d107ad22259ea24f3ff5a5a8f1a5b6717eb70..983f8cb0afacce9cad6288801c2b76588e021783 100644 >--- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >+++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp >@@ -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); >diff --git a/Source/JavaScriptCore/jit/Repatch.cpp b/Source/JavaScriptCore/jit/Repatch.cpp >index 14373bbb246718e947def6e92e00250ac4cfdae1..17acdae70e13c7521b76e580a250b296488f604a 100644 >--- a/Source/JavaScriptCore/jit/Repatch.cpp >+++ b/Source/JavaScriptCore/jit/Repatch.cpp >@@ -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; > >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm >index 2bc9febfec38578267c060c9f638cb1185581cda..f463b742d7b52cb239e4c73deb1649b5e784dcb1 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm >@@ -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 >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >index d51b6d14c00fad68d84770f3c03beb88646f2791..80f41d804a6dfa0d9124c94ec41dd11061e06489 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >@@ -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( >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >index 626989d526faeb02a85b20e95d6dcb968abb0ad0..203c6cb1f3012ff4288b8c1a35e468b6a8882d1b 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >@@ -1740,6 +1740,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( >diff --git a/Source/JavaScriptCore/runtime/Butterfly.h b/Source/JavaScriptCore/runtime/Butterfly.h >index 71fe75b9fcc6c3b4943697a9cc8e94d8be565c46..6485890d9b798b302fac7a4b5c87213887b22345 100644 >--- a/Source/JavaScriptCore/runtime/Butterfly.h >+++ b/Source/JavaScriptCore/runtime/Butterfly.h >@@ -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 >diff --git a/Source/JavaScriptCore/runtime/ButterflyInlines.h b/Source/JavaScriptCore/runtime/ButterflyInlines.h >index 59a662b9a088659edb445d091e29279e51344ac9..6a89a0d384f1e1c76a0486dc9c9707bf14d930e6 100644 >--- a/Source/JavaScriptCore/runtime/ButterflyInlines.h >+++ b/Source/JavaScriptCore/runtime/ButterflyInlines.h >@@ -33,6 +33,20 @@ > > 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); >diff --git a/Source/JavaScriptCore/runtime/ClonedArguments.cpp b/Source/JavaScriptCore/runtime/ClonedArguments.cpp >index 3ad6a769ccf35e85ec2b010669c84cc62a9ff2e5..8f461d0037ea4706e5bc79e2a5a46cb748732898 100644 >--- a/Source/JavaScriptCore/runtime/ClonedArguments.cpp >+++ b/Source/JavaScriptCore/runtime/ClonedArguments.cpp >@@ -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 = >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index 84dd3d2a4516a188f0f98a01d501004cdc725663..a6a49d731a21bda5ccf9d36b1ad65dd24a1eba0a 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -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); \ >@@ -1082,8 +1084,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) >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >index c28513567cd3a687bd03459f761dc9aff8d0007b..a371a2438332fb5e37479343e08d610ac97d039b 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h >@@ -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; >diff --git a/Source/JavaScriptCore/runtime/IndexingType.cpp b/Source/JavaScriptCore/runtime/IndexingType.cpp >index 8b5ab3c5bc80f832fa0da647eaba7545e2a174d4..dfecefcc7e4d0563a25dc10f6307506f3363c79f 100644 >--- a/Source/JavaScriptCore/runtime/IndexingType.cpp >+++ b/Source/JavaScriptCore/runtime/IndexingType.cpp >@@ -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; >diff --git a/Source/JavaScriptCore/runtime/IndexingType.h b/Source/JavaScriptCore/runtime/IndexingType.h >index 689fae264205b1e03184190b16cc851a5ab07d80..2ad4ee6e46092c83aaa783ff9d8ec30eb43fab62 100644 >--- a/Source/JavaScriptCore/runtime/IndexingType.h >+++ b/Source/JavaScriptCore/runtime/IndexingType.h >@@ -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; >diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp >index 6fd86e710e08e8c8c66411be5dfe5ddc5ae29545..ba1819f30a6095934d9add0adbb222244d01887b 100644 >--- a/Source/JavaScriptCore/runtime/JSArray.cpp >+++ b/Source/JavaScriptCore/runtime/JSArray.cpp >@@ -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); >diff --git a/Source/JavaScriptCore/runtime/JSArrayInlines.h b/Source/JavaScriptCore/runtime/JSArrayInlines.h >index 032579e9fc355c27d9ce0e0ed7ca3c89aa399dbd..cfe73baf69bcc2fd08a4d810f1f43b5c31199e59 100644 >--- a/Source/JavaScriptCore/runtime/JSArrayInlines.h >+++ b/Source/JavaScriptCore/runtime/JSArrayInlines.h >@@ -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); >+ } > } > } > >diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h >index 8808cb5aa14297921c5a2430d75fb4202887c46c..7c1f01973b6cdf33893e4cf3e6bce52d8becbaa2 100644 >--- a/Source/JavaScriptCore/runtime/JSCell.h >+++ b/Source/JavaScriptCore/runtime/JSCell.h >@@ -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; >diff --git a/Source/JavaScriptCore/runtime/JSCellInlines.h b/Source/JavaScriptCore/runtime/JSCellInlines.h >index 652369406ba152c07ea0f648da4b2c5b66fe8d1f..e1839b95212362549fa440322c8177ca995be7aa 100644 >--- a/Source/JavaScriptCore/runtime/JSCellInlines.h >+++ b/Source/JavaScriptCore/runtime/JSCellInlines.h >@@ -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) >@@ -101,6 +101,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; > } >diff --git a/Source/JavaScriptCore/runtime/JSFixedArray.h b/Source/JavaScriptCore/runtime/JSFixedArray.h >index c36f85661c186aad8e8c865c174fd668e9751f4d..1586133051c068c36470fe8c9613bd0b2158b867 100644 >--- a/Source/JavaScriptCore/runtime/JSFixedArray.h >+++ b/Source/JavaScriptCore/runtime/JSFixedArray.h >@@ -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); > } >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >index 69b537eb5d6e921e72d46850e196c0aa2df94632..75a252cb4179633c9f79e52cdbc22f1ecf1df46d 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -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); >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h >index 353733ebc6ac2fad4dcbe618ea45db2d64779b66..69b6c8f707f04193e544e3470140b56782df4c73 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h >@@ -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 >index 0000000000000000000000000000000000000000..6b0c9e17529c45d93e4b23d375df071b89e5fb85 >--- /dev/null >+++ b/Source/JavaScriptCore/runtime/JSImmutableButterfly.cpp >@@ -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 >index 0000000000000000000000000000000000000000..04f5714072145d6932ce224fc7d121ab228e4455 >--- /dev/null >+++ b/Source/JavaScriptCore/runtime/JSImmutableButterfly.h >@@ -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 >diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp >index d84f9931e01989a3632b3f8dcd46c79e839207dd..0bf868bdec8774412ee34b5c57f1df1c326f1edf 100644 >--- a/Source/JavaScriptCore/runtime/JSObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSObject.cpp >@@ -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; > } >diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h >index c811235e914d2a4b2a25e6453055fe6901335a32..4a6128a3956d87031fae236b18c033840020634b 100644 >--- a/Source/JavaScriptCore/runtime/JSObject.h >+++ b/Source/JavaScriptCore/runtime/JSObject.h >@@ -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*); >diff --git a/Source/JavaScriptCore/runtime/JSObjectInlines.h b/Source/JavaScriptCore/runtime/JSObjectInlines.h >index 4b0c8648ae20c973a4c7752f3487cb14961ed066..527fc2295fd16b2237269b7c3f784ca80938ec47 100644 >--- a/Source/JavaScriptCore/runtime/JSObjectInlines.h >+++ b/Source/JavaScriptCore/runtime/JSObjectInlines.h >@@ -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; >diff --git a/Source/JavaScriptCore/runtime/JSType.h b/Source/JavaScriptCore/runtime/JSType.h >index d86dc3b4a5d4ca9057915e3408d44f2322b9e1e4..afa7dc2fd434f468ce27024240b6a0ea3cbbab18 100644 >--- a/Source/JavaScriptCore/runtime/JSType.h >+++ b/Source/JavaScriptCore/runtime/JSType.h >@@ -47,6 +47,7 @@ enum JSType : uint8_t { > CodeBlockType, > > JSFixedArrayType, >+ JSImmutableButterflyType, > JSSourceCodeType, > JSScriptFetcherType, > JSScriptFetchParametersType, >diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h >index 940c2e5f7b94b4b885f927cb7946e53c39cb0b00..d1a1cbe4ff0a7bb0f5db6298ef23654dec9c347e 100644 >--- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h >+++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h >@@ -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); >diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp >index 00f87b0a6fa3104be6ab3fde026b9c9924f1237e..c5959b2f54e9ca6bc2b87a454ab16e9987d5917d 100644 >--- a/Source/JavaScriptCore/runtime/Structure.cpp >+++ b/Source/JavaScriptCore/runtime/Structure.cpp >@@ -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); >diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h >index 6bf4cecbd9297f70069d7b7bc376154c91657f5e..503ce4626b1102c8fab484a1e42e15fc40f68313 100644 >--- a/Source/JavaScriptCore/runtime/Structure.h >+++ b/Source/JavaScriptCore/runtime/Structure.h >@@ -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() >diff --git a/Source/JavaScriptCore/runtime/StructureIDBlob.h b/Source/JavaScriptCore/runtime/StructureIDBlob.h >index 51564ca4028bcec8ded711c53ebad8f5ce815c87..066c8adf468e07b81a7daeb604126725acf67401 100644 >--- a/Source/JavaScriptCore/runtime/StructureIDBlob.h >+++ b/Source/JavaScriptCore/runtime/StructureIDBlob.h >@@ -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; >diff --git a/Source/JavaScriptCore/runtime/StructureTransitionTable.h b/Source/JavaScriptCore/runtime/StructureTransitionTable.h >index 68a4689d0c91e881bad79cae8ea0eaa77114ba9a..437641e5ecb91efbe59cd1261fc96f1a2ed82234 100644 >--- a/Source/JavaScriptCore/runtime/StructureTransitionTable.h >+++ b/Source/JavaScriptCore/runtime/StructureTransitionTable.h >@@ -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: >diff --git a/Source/JavaScriptCore/runtime/VM.cpp b/Source/JavaScriptCore/runtime/VM.cpp >index d69b1e17e4afd749fbf7e5ddf4362cf1ae5bd46b..89d0f304e6e72ff6c7ffd3568f05507126f3b0f3 100644 >--- a/Source/JavaScriptCore/runtime/VM.cpp >+++ b/Source/JavaScriptCore/runtime/VM.cpp >@@ -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())); >diff --git a/Source/JavaScriptCore/runtime/VM.h b/Source/JavaScriptCore/runtime/VM.h >index 24a91cdbc4ddb5c3eb99ed0e32e142e9d6da7fe9..cb5227086f722db311329f9839207dd9b038620f 100644 >--- a/Source/JavaScriptCore/runtime/VM.h >+++ b/Source/JavaScriptCore/runtime/VM.h >@@ -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; >diff --git a/Source/WebCore/bindings/js/JSDOMConvertSequences.h b/Source/WebCore/bindings/js/JSDOMConvertSequences.h >index b07180277dc4388fc25b2ba14d68be246b310ef5..3ae939e452a22b7859eae9ab951080e00645edce 100644 >--- a/Source/WebCore/bindings/js/JSDOMConvertSequences.h >+++ b/Source/WebCore/bindings/js/JSDOMConvertSequences.h >@@ -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(); > >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index a261cb3715e8ef9e3ac9e7787282de6b1e04fba3..6c56c199df9298fc565185be5514ce888afc80d0 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -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-18 Commit Queue <commit-queue@webkit.org> > > Unreviewed, rolling out r231982. >diff --git a/LayoutTests/js/slow-stress/script-tests/variadic-closure-call.js b/LayoutTests/js/slow-stress/script-tests/variadic-closure-call.js >index 220a708f1cd7cb5834ea6c678bf2881b3726ab1a..5e802f8ff86b2f429724198f34eaf5168524495b 100644 >--- a/LayoutTests/js/slow-stress/script-tests/variadic-closure-call.js >+++ b/LayoutTests/js/slow-stress/script-tests/variadic-closure-call.js >@@ -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); > } > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index b6ad0467d35298f789aae755fbad821b258773b6..4a76af9173e600eaa7345c8dd7449f0b70b743ef 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -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-18 Keith Miller <keith_miller@apple.com> > > op_in should mark if it sees out of bounds accesses >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 >index 0000000000000000000000000000000000000000..03c6170663a10e3cce8cdbf765b8decd3c7f7721 >--- /dev/null >+++ b/JSTests/stress/cow-convert-contiguous-to-array-storage.js >@@ -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 >index 0000000000000000000000000000000000000000..9782c09f9dc8ff19b228285d7a097f92c7e32e01 >--- /dev/null >+++ b/JSTests/stress/cow-convert-double-to-array-storage.js >@@ -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 >index 0000000000000000000000000000000000000000..55896b1affa73e935605d46db727b53bd6fe16e4 >--- /dev/null >+++ b/JSTests/stress/cow-convert-double-to-contiguous.js >@@ -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 >index 0000000000000000000000000000000000000000..90a744da754fc3ba6554c66496c1f6e88bab2e34 >--- /dev/null >+++ b/JSTests/stress/cow-convert-int32-to-array-storage.js >@@ -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 >index 0000000000000000000000000000000000000000..62e321b00a4f9fc7c10f9087ecf090a6f0cd0603 >--- /dev/null >+++ b/JSTests/stress/cow-convert-int32-to-contiguous.js >@@ -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 >index 0000000000000000000000000000000000000000..4dd378729fe90e7b21d31dafaa17f381b2b0a5fd >--- /dev/null >+++ b/JSTests/stress/cow-convert-int32-to-double.js >@@ -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 >index 0000000000000000000000000000000000000000..6bfa7b25cb68808e67ae34ee9699265fdcc9d943 >--- /dev/null >+++ b/JSTests/stress/put-on-cow-prototype.js >@@ -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(); >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
commit-queue
:
commit-queue-
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 185003
:
340710
|
340771
|
340776
|
340778
|
340780
|
340781
|
340782
|
340893
|
340894
|
340900
|
340929
|
340942
| 340989 |
340993