Ignore:
Timestamp:
Sep 12, 2012, 9:18:52 PM (13 years ago)
Author:
[email protected]
Message:

JSC should have property butterflies
https://bugs.webkit.org/show_bug.cgi?id=91933

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

This changes the JSC object model. Previously, all objects had fast lookup for
named properties. Integer indexed properties were only fast if you used a
JSArray. With this change, all objects have fast indexed properties. This is
accomplished without any space overhead by using a bidirectional object layout,
aka butterflies. Each JSObject has a m_butterfly pointer where previously it
had a m_outOfLineStorage pointer. To the left of the location pointed to by
m_butterfly, we place all named out-of-line properties. To the right, we place
all indexed properties along with indexing meta-data. Though, some indexing
meta-data is placed in the 8-byte word immediately left of the pointed-to
location; this is in anticipation of the indexing meta-data being small enough
in the common case that m_butterfly always points to the first indexed
property.

This is performance neutral, except on tests that use indexed properties on
plain objects, where the speed-up is in excess of an order of magnitude.

One notable aspect of what this change brings is that it allows indexing
storage to morph over time. Currently this is only used to allow all non-array
objects to start out without any indexed storage. But it could be used for
some kinds of array type inference in the future.

  • API/JSCallbackObject.h:

(JSCallbackObject):

  • API/JSCallbackObjectFunctions.h:

(JSC::::getOwnPropertySlotByIndex):
(JSC):
(JSC::::getOwnNonIndexPropertyNames):

  • API/JSObjectRef.cpp:
  • CMakeLists.txt:
  • GNUmakefile.list.am:
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Target.pri:
  • bytecode/ArrayProfile.h:

(JSC):
(JSC::arrayModeFromStructure):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitDirectPutById):

  • dfg/DFGAbstractState.cpp:

(JSC::DFG::AbstractState::execute):

  • dfg/DFGAdjacencyList.h:

(JSC::DFG::AdjacencyList::AdjacencyList):
(AdjacencyList):

  • dfg/DFGArrayMode.cpp:

(JSC::DFG::fromObserved):
(JSC::DFG::modeAlreadyChecked):
(JSC::DFG::modeToString):

  • dfg/DFGArrayMode.h:

(DFG):
(JSC::DFG::modeUsesButterfly):
(JSC::DFG::modeIsJSArray):
(JSC::DFG::isInBoundsAccess):
(JSC::DFG::modeSupportsLength):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleGetByOffset):
(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCSEPhase.cpp:

(JSC::DFG::CSEPhase::getPropertyStorageLoadElimination):
(JSC::DFG::CSEPhase::performNodeCSE):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::addNode):
(FixupPhase):
(JSC::DFG::FixupPhase::checkArray):

  • dfg/DFGGraph.h:

(JSC::DFG::Graph::byValIsPure):

  • dfg/DFGNode.h:

(JSC::DFG::Node::Node):
(Node):

  • dfg/DFGNodeType.h:

(DFG):

  • dfg/DFGOperations.cpp:

(JSC::DFG::putByVal):

  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::PredictionPropagationPhase::propagate):

  • dfg/DFGRepatch.cpp:

(JSC::DFG::generateProtoChainAccessStub):
(JSC::DFG::tryCacheGetByID):
(JSC::DFG::tryBuildGetByIDList):
(JSC::DFG::emitPutReplaceStub):
(JSC::DFG::emitPutTransitionStub):
(JSC::DFG::tryBuildPutByIdList):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::checkArray):
(JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
(JSC::DFG::SpeculativeJIT::compileAllocatePropertyStorage):
(JSC::DFG::SpeculativeJIT::compileReallocatePropertyStorage):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callOperation):
(JSC::DFG::SpeculativeJIT::emitAllocateBasicJSObject):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGStructureCheckHoistingPhase.cpp:

(JSC::DFG::StructureCheckHoistingPhase::run):

  • heap/CopiedSpace.h:

(CopiedSpace):

  • jit/JIT.h:
  • jit/JITInlineMethods.h:

(JSC::JIT::emitAllocateBasicJSObject):
(JSC::JIT::emitAllocateBasicStorage):
(JSC::JIT::emitAllocateJSArray):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_new_array):
(JSC::JIT::emitSlow_op_new_array):

  • jit/JITPropertyAccess.cpp:

(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::compileGetDirectOffset):
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::compileGetByIdHotPath):
(JSC::JIT::emit_op_put_by_id):
(JSC::JIT::compilePutDirectOffset):
(JSC::JIT::privateCompilePatchGetArrayLength):

  • jit/JITPropertyAccess32_64.cpp:

(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::compileGetByIdHotPath):
(JSC::JIT::emit_op_put_by_id):
(JSC::JIT::compilePutDirectOffset):
(JSC::JIT::compileGetDirectOffset):
(JSC::JIT::privateCompilePatchGetArrayLength):

  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):

  • jsc.cpp:
  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • runtime/Arguments.cpp:

(JSC::Arguments::deletePropertyByIndex):
(JSC::Arguments::defineOwnProperty):

  • runtime/ArrayConstructor.cpp:
  • runtime/ArrayConventions.h: Added.

(JSC):
(JSC::isDenseEnoughForVector):
(JSC::indexingHeaderForArray):
(JSC::baseIndexingHeaderForArray):

  • runtime/ArrayPrototype.cpp:

(JSC::ArrayPrototype::create):
(JSC):
(JSC::ArrayPrototype::ArrayPrototype):
(JSC::arrayProtoFuncToString):
(JSC::arrayProtoFuncJoin):
(JSC::arrayProtoFuncSort):
(JSC::arrayProtoFuncFilter):
(JSC::arrayProtoFuncMap):
(JSC::arrayProtoFuncEvery):
(JSC::arrayProtoFuncForEach):
(JSC::arrayProtoFuncSome):
(JSC::arrayProtoFuncReduce):
(JSC::arrayProtoFuncReduceRight):

  • runtime/ArrayPrototype.h:

(ArrayPrototype):
(JSC::ArrayPrototype::createStructure):

  • runtime/ArrayStorage.h: Added.

(JSC):
(ArrayStorage):
(JSC::ArrayStorage::ArrayStorage):
(JSC::ArrayStorage::from):
(JSC::ArrayStorage::butterfly):
(JSC::ArrayStorage::indexingHeader):
(JSC::ArrayStorage::length):
(JSC::ArrayStorage::setLength):
(JSC::ArrayStorage::vectorLength):
(JSC::ArrayStorage::setVectorLength):
(JSC::ArrayStorage::copyHeaderFromDuringGC):
(JSC::ArrayStorage::inSparseMode):
(JSC::ArrayStorage::lengthOffset):
(JSC::ArrayStorage::vectorLengthOffset):
(JSC::ArrayStorage::numValuesInVectorOffset):
(JSC::ArrayStorage::vectorOffset):
(JSC::ArrayStorage::indexBiasOffset):
(JSC::ArrayStorage::sparseMapOffset):
(JSC::ArrayStorage::sizeFor):

  • runtime/Butterfly.h: Added.

(JSC):
(Butterfly):
(JSC::Butterfly::Butterfly):
(JSC::Butterfly::totalSize):
(JSC::Butterfly::fromBase):
(JSC::Butterfly::offsetOfIndexingHeader):
(JSC::Butterfly::offsetOfPublicLength):
(JSC::Butterfly::offsetOfVectorLength):
(JSC::Butterfly::indexingHeader):
(JSC::Butterfly::propertyStorage):
(JSC::Butterfly::indexingPayload):
(JSC::Butterfly::arrayStorage):
(JSC::Butterfly::offsetOfPropertyStorage):
(JSC::Butterfly::indexOfPropertyStorage):
(JSC::Butterfly::base):

  • runtime/ButterflyInlineMethods.h: Added.

(JSC):
(JSC::Butterfly::createUninitialized):
(JSC::Butterfly::create):
(JSC::Butterfly::createUninitializedDuringCollection):
(JSC::Butterfly::base):
(JSC::Butterfly::growPropertyStorage):
(JSC::Butterfly::growArrayRight):
(JSC::Butterfly::resizeArray):
(JSC::Butterfly::unshift):
(JSC::Butterfly::shift):

  • runtime/ClassInfo.h:

(MethodTable):
(JSC):

  • runtime/IndexingHeader.h: Added.

(JSC):
(IndexingHeader):
(JSC::IndexingHeader::offsetOfIndexingHeader):
(JSC::IndexingHeader::offsetOfPublicLength):
(JSC::IndexingHeader::offsetOfVectorLength):
(JSC::IndexingHeader::IndexingHeader):
(JSC::IndexingHeader::vectorLength):
(JSC::IndexingHeader::setVectorLength):
(JSC::IndexingHeader::publicLength):
(JSC::IndexingHeader::setPublicLength):
(JSC::IndexingHeader::from):
(JSC::IndexingHeader::fromEndOf):
(JSC::IndexingHeader::propertyStorage):
(JSC::IndexingHeader::arrayStorage):
(JSC::IndexingHeader::butterfly):

  • runtime/IndexingHeaderInlineMethods.h: Added.

(JSC):
(JSC::IndexingHeader::preCapacity):
(JSC::IndexingHeader::indexingPayloadSizeInBytes):

  • runtime/IndexingType.h: Added.

(JSC):
(JSC::hasIndexingHeader):

  • runtime/JSActivation.cpp:

(JSC::JSActivation::JSActivation):
(JSC::JSActivation::visitChildren):
(JSC::JSActivation::getOwnNonIndexPropertyNames):

  • runtime/JSActivation.h:

(JSActivation):
(JSC::JSActivation::tearOff):

  • runtime/JSArray.cpp:

(JSC):
(JSC::createArrayButterflyInDictionaryIndexingMode):
(JSC::JSArray::setLengthWritable):
(JSC::JSArray::defineOwnProperty):
(JSC::JSArray::getOwnPropertySlot):
(JSC::JSArray::getOwnPropertyDescriptor):
(JSC::JSArray::put):
(JSC::JSArray::deleteProperty):
(JSC::JSArray::getOwnNonIndexPropertyNames):
(JSC::JSArray::unshiftCountSlowCase):
(JSC::JSArray::setLength):
(JSC::JSArray::pop):
(JSC::JSArray::push):
(JSC::JSArray::shiftCount):
(JSC::JSArray::unshiftCount):
(JSC::JSArray::sortNumeric):
(JSC::JSArray::sort):
(JSC::JSArray::fillArgList):
(JSC::JSArray::copyToArguments):
(JSC::JSArray::compactForSorting):

  • runtime/JSArray.h:

(JSC):
(JSArray):
(JSC::JSArray::JSArray):
(JSC::JSArray::length):
(JSC::JSArray::createStructure):
(JSC::JSArray::isLengthWritable):
(JSC::createArrayButterfly):
(JSC::JSArray::create):
(JSC::JSArray::tryCreateUninitialized):

  • runtime/JSBoundFunction.cpp:

(JSC::boundFunctionCall):
(JSC::boundFunctionConstruct):
(JSC::JSBoundFunction::finishCreation):

  • runtime/JSCell.cpp:

(JSC::JSCell::getOwnNonIndexPropertyNames):
(JSC):

  • runtime/JSCell.h:

(JSCell):

  • runtime/JSFunction.cpp:

(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::getOwnPropertyDescriptor):
(JSC::JSFunction::getOwnNonIndexPropertyNames):
(JSC::JSFunction::defineOwnProperty):

  • runtime/JSFunction.h:

(JSFunction):

  • runtime/JSGlobalData.cpp:

(JSC::JSGlobalData::JSGlobalData):

  • runtime/JSGlobalData.h:

(JSGlobalData):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::reset):

  • runtime/JSONObject.cpp:

(JSC::Stringifier::Holder::appendNextProperty):
(JSC::Walker::walk):

  • runtime/JSObject.cpp:

(JSC):
(JSC::JSObject::visitButterfly):
(JSC::JSObject::visitChildren):
(JSC::JSFinalObject::visitChildren):
(JSC::JSObject::getOwnPropertySlotByIndex):
(JSC::JSObject::put):
(JSC::JSObject::putByIndex):
(JSC::JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists):
(JSC::JSObject::enterDictionaryIndexingMode):
(JSC::JSObject::createArrayStorage):
(JSC::JSObject::createInitialArrayStorage):
(JSC::JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode):
(JSC::JSObject::putDirectAccessor):
(JSC::JSObject::deleteProperty):
(JSC::JSObject::deletePropertyByIndex):
(JSC::JSObject::getOwnPropertyNames):
(JSC::JSObject::getOwnNonIndexPropertyNames):
(JSC::JSObject::preventExtensions):
(JSC::JSObject::fillGetterPropertySlot):
(JSC::JSObject::putIndexedDescriptor):
(JSC::JSObject::defineOwnIndexedProperty):
(JSC::JSObject::allocateSparseIndexMap):
(JSC::JSObject::deallocateSparseIndexMap):
(JSC::JSObject::putByIndexBeyondVectorLengthWithArrayStorage):
(JSC::JSObject::putByIndexBeyondVectorLength):
(JSC::JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage):
(JSC::JSObject::putDirectIndexBeyondVectorLength):
(JSC::JSObject::getNewVectorLength):
(JSC::JSObject::increaseVectorLength):
(JSC::JSObject::checkIndexingConsistency):
(JSC::JSObject::growOutOfLineStorage):
(JSC::JSObject::getOwnPropertyDescriptor):
(JSC::putDescriptor):
(JSC::JSObject::putDirectMayBeIndex):
(JSC::JSObject::defineOwnNonIndexProperty):
(JSC::JSObject::defineOwnProperty):
(JSC::JSObject::getOwnPropertySlotSlow):

  • runtime/JSObject.h:

(JSC::JSObject::getArrayLength):
(JSObject):
(JSC::JSObject::getVectorLength):
(JSC::JSObject::putDirectIndex):
(JSC::JSObject::canGetIndexQuickly):
(JSC::JSObject::getIndexQuickly):
(JSC::JSObject::canSetIndexQuickly):
(JSC::JSObject::setIndexQuickly):
(JSC::JSObject::initializeIndex):
(JSC::JSObject::completeInitialization):
(JSC::JSObject::inSparseIndexingMode):
(JSC::JSObject::butterfly):
(JSC::JSObject::outOfLineStorage):
(JSC::JSObject::offsetForLocation):
(JSC::JSObject::indexingShouldBeSparse):
(JSC::JSObject::butterflyOffset):
(JSC::JSObject::butterflyAddress):
(JSC::JSObject::arrayStorage):
(JSC::JSObject::arrayStorageOrZero):
(JSC::JSObject::ensureArrayStorage):
(JSC::JSObject::checkIndexingConsistency):
(JSC::JSNonFinalObject::JSNonFinalObject):
(JSC):
(JSC::JSObject::setButterfly):
(JSC::JSObject::setButterflyWithoutChangingStructure):
(JSC::JSObject::JSObject):
(JSC::JSObject::inlineGetOwnPropertySlot):
(JSC::JSObject::putDirectInternal):
(JSC::JSObject::setStructureAndReallocateStorageIfNecessary):
(JSC::JSObject::putDirectWithoutTransition):
(JSC::offsetInButterfly):
(JSC::offsetRelativeToPatchedStorage):
(JSC::indexRelativeToBase):
(JSC::offsetRelativeToBase):

  • runtime/JSPropertyNameIterator.cpp:

(JSC::JSPropertyNameIterator::create):

  • runtime/JSSymbolTableObject.cpp:

(JSC::JSSymbolTableObject::getOwnNonIndexPropertyNames):

  • runtime/JSSymbolTableObject.h:

(JSSymbolTableObject):

  • runtime/JSTypeInfo.h:

(JSC):
(JSC::TypeInfo::interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero):
(JSC::TypeInfo::overridesGetPropertyNames):

  • runtime/LiteralParser.cpp:

(JSC::::parse):

  • runtime/ObjectConstructor.cpp:
  • runtime/ObjectPrototype.cpp:

(JSC::ObjectPrototype::ObjectPrototype):
(JSC):

  • runtime/ObjectPrototype.h:

(ObjectPrototype):

  • runtime/PropertyOffset.h:

(JSC::offsetInOutOfLineStorage):

  • runtime/PropertyStorage.h: Added.

(JSC):

  • runtime/PutDirectIndexMode.h: Added.

(JSC):

  • runtime/RegExpMatchesArray.cpp:

(JSC::RegExpMatchesArray::RegExpMatchesArray):
(JSC):
(JSC::RegExpMatchesArray::create):
(JSC::RegExpMatchesArray::finishCreation):

  • runtime/RegExpMatchesArray.h:

(RegExpMatchesArray):
(JSC::RegExpMatchesArray::createStructure):

  • runtime/RegExpObject.cpp:

(JSC::RegExpObject::getOwnNonIndexPropertyNames):

  • runtime/RegExpObject.h:

(RegExpObject):

  • runtime/Reject.h: Added.

(JSC):
(JSC::reject):

  • runtime/SparseArrayValueMap.cpp: Added.

(JSC):

  • runtime/SparseArrayValueMap.h: Added.

(JSC):
(SparseArrayEntry):
(JSC::SparseArrayEntry::SparseArrayEntry):
(SparseArrayValueMap):
(JSC::SparseArrayValueMap::sparseMode):
(JSC::SparseArrayValueMap::setSparseMode):
(JSC::SparseArrayValueMap::lengthIsReadOnly):
(JSC::SparseArrayValueMap::setLengthIsReadOnly):
(JSC::SparseArrayValueMap::find):
(JSC::SparseArrayValueMap::remove):
(JSC::SparseArrayValueMap::notFound):
(JSC::SparseArrayValueMap::isEmpty):
(JSC::SparseArrayValueMap::contains):
(JSC::SparseArrayValueMap::size):
(JSC::SparseArrayValueMap::begin):
(JSC::SparseArrayValueMap::end):

  • runtime/SparseArrayValueMapInlineMethods.h: Added.

(JSC):
(JSC::SparseArrayValueMap::SparseArrayValueMap):
(JSC::SparseArrayValueMap::~SparseArrayValueMap):
(JSC::SparseArrayValueMap::finishCreation):
(JSC::SparseArrayValueMap::create):
(JSC::SparseArrayValueMap::destroy):
(JSC::SparseArrayValueMap::createStructure):
(JSC::SparseArrayValueMap::add):
(JSC::SparseArrayValueMap::putEntry):
(JSC::SparseArrayValueMap::putDirect):
(JSC::SparseArrayEntry::get):
(JSC::SparseArrayEntry::getNonSparseMode):
(JSC::SparseArrayValueMap::visitChildren):

  • runtime/StorageBarrier.h: Removed.
  • runtime/StringObject.cpp:

(JSC::StringObject::putByIndex):
(JSC):
(JSC::StringObject::deletePropertyByIndex):

  • runtime/StringObject.h:

(StringObject):

  • runtime/StringPrototype.cpp:
  • runtime/Structure.cpp:

(JSC::Structure::Structure):
(JSC::Structure::materializePropertyMap):
(JSC::Structure::nonPropertyTransition):
(JSC):

  • runtime/Structure.h:

(Structure):
(JSC::Structure::indexingType):
(JSC::Structure::indexingTypeIncludingHistory):
(JSC::Structure::indexingTypeOffset):
(JSC::Structure::create):

  • runtime/StructureTransitionTable.h:

(JSC):
(JSC::toAttributes):
(JSC::newIndexingType):
(JSC::StructureTransitionTable::Hash::hash):

  • tests/mozilla/js1_6/Array/regress-304828.js:

Source/WebCore:

Teach the DOM that to intercept get/put on indexed properties, you now have
to override getOwnPropertySlotByIndex and putByIndex.

No new tests because no new behavior. One test was rebased because indexed
property iteration order now matches other engines (indexed properties always
come first).

  • bindings/js/ArrayValue.cpp:

(WebCore::ArrayValue::get):

  • bindings/js/JSBlobCustom.cpp:

(WebCore::JSBlobConstructor::constructJSBlob):

  • bindings/js/JSCanvasRenderingContext2DCustom.cpp:

(WebCore::JSCanvasRenderingContext2D::setWebkitLineDash):

  • bindings/js/JSDOMStringListCustom.cpp:

(WebCore::toDOMStringList):

  • bindings/js/JSDOMStringMapCustom.cpp:

(WebCore::JSDOMStringMap::deletePropertyByIndex):
(WebCore):

  • bindings/js/JSDOMWindowCustom.cpp:

(WebCore::JSDOMWindow::getOwnPropertySlot):
(WebCore::JSDOMWindow::getOwnPropertySlotByIndex):
(WebCore):
(WebCore::JSDOMWindow::putByIndex):
(WebCore::JSDOMWindow::deletePropertyByIndex):

  • bindings/js/JSDOMWindowShell.cpp:

(WebCore::JSDOMWindowShell::getOwnPropertySlotByIndex):
(WebCore):
(WebCore::JSDOMWindowShell::putByIndex):
(WebCore::JSDOMWindowShell::deletePropertyByIndex):

  • bindings/js/JSDOMWindowShell.h:

(JSDOMWindowShell):

  • bindings/js/JSHistoryCustom.cpp:

(WebCore::JSHistory::deletePropertyByIndex):
(WebCore):

  • bindings/js/JSInspectorFrontendHostCustom.cpp:

(WebCore::populateContextMenuItems):

  • bindings/js/JSLocationCustom.cpp:

(WebCore::JSLocation::deletePropertyByIndex):
(WebCore):

  • bindings/js/JSStorageCustom.cpp:

(WebCore::JSStorage::deletePropertyByIndex):
(WebCore):

  • bindings/js/JSWebSocketCustom.cpp:

(WebCore::JSWebSocketConstructor::constructJSWebSocket):

  • bindings/js/ScriptValue.cpp:

(WebCore::jsToInspectorValue):

  • bindings/js/SerializedScriptValue.cpp:

(WebCore::CloneSerializer::serialize):

  • bindings/scripts/CodeGeneratorJS.pm:

(GenerateHeader):
(GenerateImplementation):

  • bridge/runtime_array.cpp:

(JSC::RuntimeArray::RuntimeArray):

  • bridge/runtime_array.h:

(JSC::RuntimeArray::createStructure):
(RuntimeArray):

LayoutTests:

Modify the JSON test to indicate that iterating over properties now returns
indexed properties first. This is a behavior change that makes us more
compliant with other implementations.

Also check in new expected file for the edge cases of indexed property access
with prototype accessors. This changeset introduces a known regression in that
department, which is tracked here: https://bugs.webkit.org/show_bug.cgi?id=96596

  • fast/js/resources/JSON-stringify.js:
  • platform/mac/fast/js/primitive-property-access-edge-cases-expected.txt: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/JSArray.cpp

    r127505 r128400  
    11/*
    22 *  Copyright (C) 1999-2000 Harri Porten ([email protected])
    3  *  Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
     3 *  Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
    44 *  Copyright (C) 2003 Peter Kelly ([email protected])
    55 *  Copyright (C) 2006 Alexey Proskuryakov ([email protected])
     
    2525
    2626#include "ArrayPrototype.h"
     27#include "ButterflyInlineMethods.h"
    2728#include "CopiedSpace.h"
    2829#include "CopiedSpaceInlineMethods.h"
     
    3132#include "Executable.h"
    3233#include "GetterSetter.h"
     34#include "IndexingHeaderInlineMethods.h"
    3335#include "PropertyNameArray.h"
     36#include "Reject.h"
     37#include "SparseArrayValueMapInlineMethods.h"
    3438#include <wtf/AVLTree.h>
    3539#include <wtf/Assertions.h>
     
    4650ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
    4751
    48 // Overview of JSArray
    49 //
    50 // Properties of JSArray objects may be stored in one of three locations:
    51 //   * The regular JSObject property map.
    52 //   * A storage vector.
    53 //   * A sparse map of array entries.
    54 //
    55 // Properties with non-numeric identifiers, with identifiers that are not representable
    56 // as an unsigned integer, or where the value is greater than  MAX_ARRAY_INDEX
    57 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
    58 // integer) are not considered array indices and will be stored in the JSObject property map.
    59 //
    60 // All properties with a numeric identifer, representable as an unsigned integer i,
    61 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
    62 // storage vector or the sparse map.  An array index i will be handled in the following
    63 // fashion:
    64 //
    65 //   * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector,
    66 //     unless the array is in SparseMode in which case all properties go into the map.
    67 //   * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
    68 //     be stored in the storage vector or in the sparse array, depending on the density of
    69 //     data that would be stored in the vector (a vector being used where at least
    70 //     (1 / minDensityMultiplier) of the entries would be populated).
    71 //   * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
    72 //     in the sparse array.
    73 
    74 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
    75 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
    76 // size calculation cannot overflow.  (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
    77 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
    78 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
    79 
    80 // These values have to be macros to be used in max() and min() without introducing
    81 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
    82 #define MIN_SPARSE_ARRAY_INDEX 10000U
    83 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
    84 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
    85 #define MAX_ARRAY_INDEX 0xFFFFFFFEU
    86 
    87 // The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate
    88 // for an array that was created with a sepcified length (e.g. a = new Array(123))
    89 #define BASE_VECTOR_LEN 4U
    90    
    91 // The upper bound to the size we'll grow a zero length array when the first element
    92 // is added.
    93 #define FIRST_VECTOR_GROW 4U
    94 
    95 // Our policy for when to use a vector and when to use a sparse map.
    96 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
    97 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
    98 // as long as it is 1/8 full. If more sparse than that, we use a map.
    99 static const unsigned minDensityMultiplier = 8;
    100 
    10152const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
    10253
    103 // We keep track of the size of the last array after it was grown.  We use this
    104 // as a simple heuristic for as the value to grow the next array from size 0.
    105 // This value is capped by the constant FIRST_VECTOR_GROW defined above.
    106 static unsigned lastArraySize = 0;
    107 
    108 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
    109 {
    110     return length <= MIN_SPARSE_ARRAY_INDEX || length / minDensityMultiplier <= numValues;
    111 }
    112 
    113 static bool reject(ExecState* exec, bool throwException, const char* message)
    114 {
    115     if (throwException)
    116         throwTypeError(exec, ASCIILiteral(message));
    117     return false;
    118 }
    119 
    120 #if !CHECK_ARRAY_CONSISTENCY
    121 
    122 inline void JSArray::checkConsistency(ConsistencyCheckType)
    123 {
    124 }
    125 
     54Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData& globalData, unsigned initialLength)
     55{
     56    Butterfly* butterfly = Butterfly::create(
     57        globalData, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
     58    ArrayStorage* storage = butterfly->arrayStorage();
     59    storage->setLength(initialLength);
     60    storage->setVectorLength(0);
     61    storage->m_indexBias = 0;
     62    storage->m_sparseMap.clear();
     63    storage->m_numValuesInVector = 0;
     64#if CHECK_ARRAY_CONSISTENCY
     65    storage->m_initializationIndex = 0;
     66    storage->m_inCompactInitialization = 0;
    12667#endif
    127 
    128 void JSArray::finishCreation(JSGlobalData& globalData, unsigned initialLength)
    129 {
    130     Base::finishCreation(globalData);
    131     ASSERT(inherits(&s_info));
    132 
    133     unsigned initialVectorLength = BASE_VECTOR_LEN;
    134     unsigned initialStorageSize = storageSize(initialVectorLength);
    135 
    136     void* newStorage = 0;
    137     if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
    138         CRASH();
    139    
    140     m_storage = static_cast<ArrayStorage*>(newStorage);
    141     m_storage->m_allocBase = m_storage;
    142     m_storage->m_length = initialLength;
    143     m_vectorLength = initialVectorLength;
    144     m_storage->m_numValuesInVector = 0;
    145 #if CHECK_ARRAY_CONSISTENCY
    146     m_storage->m_inCompactInitialization = false;
    147 #endif
    148 
    149     checkConsistency();
    150 }
    151 
    152 JSArray* JSArray::tryFinishCreationUninitialized(JSGlobalData& globalData, unsigned initialLength)
    153 {
    154     Base::finishCreation(globalData);
    155     ASSERT(inherits(&s_info));
    156 
    157     // Check for lengths larger than we can handle with a vector.
    158     if (initialLength > MAX_STORAGE_VECTOR_LENGTH)
    159         return 0;
    160 
    161     unsigned initialVectorLength = max(initialLength, BASE_VECTOR_LEN);
    162     unsigned initialStorageSize = storageSize(initialVectorLength);
    163 
    164     void* newStorage = 0;
    165     if (!globalData.heap.tryAllocateStorage(initialStorageSize, &newStorage))
    166         CRASH();
    167    
    168     m_storage = static_cast<ArrayStorage*>(newStorage);
    169     m_storage->m_allocBase = m_storage;
    170     m_storage->m_length = initialLength;
    171     m_vectorLength = initialVectorLength;
    172     m_storage->m_numValuesInVector = initialLength;
    173 
    174 #if CHECK_ARRAY_CONSISTENCY
    175     m_storage->m_initializationIndex = 0;
    176     m_storage->m_inCompactInitialization = true;
    177 #endif
    178 
    179     return this;
    180 }
    181 
    182 // This function can be called multiple times on the same object.
    183 void JSArray::finalize(JSCell* cell)
    184 {
    185     JSArray* thisObject = jsCast<JSArray*>(cell);
    186     thisObject->checkConsistency(DestructorConsistencyCheck);
    187     thisObject->deallocateSparseMap();
    188 }
    189 
    190 inline SparseArrayValueMap::AddResult SparseArrayValueMap::add(JSArray* array, unsigned i)
    191 {
    192     SparseArrayEntry entry;
    193     entry.setWithoutWriteBarrier(jsUndefined());
    194 
    195     AddResult result = m_map.add(i, entry);
    196     size_t capacity = m_map.capacity();
    197     if (capacity != m_reportedCapacity) {
    198         Heap::heap(array)->reportExtraMemoryCost((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier<Unknown>)));
    199         m_reportedCapacity = capacity;
    200     }
    201     return result;
    202 }
    203 
    204 inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value, bool shouldThrow)
    205 {
    206     AddResult result = add(array, i);
    207     SparseArrayEntry& entry = result.iterator->second;
    208 
    209     // To save a separate find & add, we first always add to the sparse map.
    210     // In the uncommon case that this is a new property, and the array is not
    211     // extensible, this is not the right thing to have done - so remove again.
    212     if (result.isNewEntry && !array->isExtensible()) {
    213         remove(result.iterator);
    214         if (shouldThrow)
    215             throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
    216         return;
    217     }
    218 
    219     if (!(entry.attributes & Accessor)) {
    220         if (entry.attributes & ReadOnly) {
    221             if (shouldThrow)
    222                 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
    223             return;
    224         }
    225 
    226         entry.set(exec->globalData(), array, value);
    227         return;
    228     }
    229 
    230     JSValue accessor = entry.Base::get();
    231     ASSERT(accessor.isGetterSetter());
    232     JSObject* setter = asGetterSetter(accessor)->setter();
    233    
    234     if (!setter) {
    235         if (shouldThrow)
    236             throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
    237         return;
    238     }
    239 
    240     CallData callData;
    241     CallType callType = setter->methodTable()->getCallData(setter, callData);
    242     MarkedArgumentBuffer args;
    243     args.append(value);
    244     call(exec, setter, callType, callData, array, args);
    245 }
    246 
    247 inline bool SparseArrayValueMap::putDirect(ExecState* exec, JSArray* array, unsigned i, JSValue value, PutDirectIndexMode mode)
    248 {
    249     AddResult result = add(array, i);
    250     SparseArrayEntry& entry = result.iterator->second;
    251 
    252     // To save a separate find & add, we first always add to the sparse map.
    253     // In the uncommon case that this is a new property, and the array is not
    254     // extensible, this is not the right thing to have done - so remove again.
    255     if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isExtensible()) {
    256         remove(result.iterator);
    257         return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
    258     }
    259 
    260     entry.attributes = 0;
    261     entry.set(exec->globalData(), array, value);
    262     return true;
    263 }
    264 
    265 inline void SparseArrayEntry::get(PropertySlot& slot) const
    266 {
    267     JSValue value = Base::get();
    268     ASSERT(value);
    269 
    270     if (LIKELY(!value.isGetterSetter())) {
    271         slot.setValue(value);
    272         return;
    273     }
    274 
    275     JSObject* getter = asGetterSetter(value)->getter();
    276     if (!getter) {
    277         slot.setUndefined();
    278         return;
    279     }
    280 
    281     slot.setGetterSlot(getter);
    282 }
    283 
    284 inline void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
    285 {
    286     descriptor.setDescriptor(Base::get(), attributes);
    287 }
    288 
    289 inline JSValue SparseArrayEntry::get(ExecState* exec, JSArray* array) const
    290 {
    291     JSValue result = Base::get();
    292     ASSERT(result);
    293 
    294     if (LIKELY(!result.isGetterSetter()))
    295         return result;
    296 
    297     JSObject* getter = asGetterSetter(result)->getter();
    298     if (!getter)
    299         return jsUndefined();
    300 
    301     CallData callData;
    302     CallType callType = getter->methodTable()->getCallData(getter, callData);
    303     return call(exec, getter, callType, callData, array, exec->emptyList());
    304 }
    305 
    306 inline JSValue SparseArrayEntry::getNonSparseMode() const
    307 {
    308     ASSERT(!attributes);
    309     return Base::get();
    310 }
    311 
    312 inline void SparseArrayValueMap::visitChildren(SlotVisitor& visitor)
    313 {
    314     iterator end = m_map.end();
    315     for (iterator it = m_map.begin(); it != end; ++it)
    316         visitor.append(&it->second);
    317 }
    318 
    319 void JSArray::allocateSparseMap(JSGlobalData& globalData)
    320 {
    321     m_sparseValueMap = new SparseArrayValueMap;
    322     globalData.heap.addFinalizer(this, finalize);
    323 }
    324 
    325 void JSArray::deallocateSparseMap()
    326 {
    327     delete m_sparseValueMap;
    328     m_sparseValueMap = 0;
    329 }
    330 
    331 void JSArray::enterDictionaryMode(JSGlobalData& globalData)
    332 {
    333     ArrayStorage* storage = m_storage;
    334     SparseArrayValueMap* map = m_sparseValueMap;
    335 
    336     if (!map) {
    337         allocateSparseMap(globalData);
    338         map = m_sparseValueMap;
    339     }
    340 
    341     if (map->sparseMode())
    342         return;
    343 
    344     map->setSparseMode();
    345 
    346     unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
    347     for (unsigned i = 0; i < usedVectorLength; ++i) {
    348         JSValue value = storage->m_vector[i].get();
    349         // This will always be a new entry in the map, so no need to check we can write,
    350         // and attributes are default so no need to set them.
    351         if (value)
    352             map->add(this, i).iterator->second.set(globalData, this, value);
    353     }
    354 
    355     void* newRawStorage = 0;
    356     if (!globalData.heap.tryAllocateStorage(storageSize(0), &newRawStorage))
    357         CRASH();
    358    
    359     ArrayStorage* newStorage = static_cast<ArrayStorage*>(newRawStorage);
    360     memcpy(newStorage, m_storage, storageSize(0));
    361     newStorage->m_allocBase = newStorage;
    362     m_storage = newStorage;
    363     m_indexBias = 0;
    364     m_vectorLength = 0;
    365 }
    366 
    367 void JSArray::putDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
    368 {
    369     if (descriptor.isDataDescriptor()) {
    370         if (descriptor.value())
    371             entryInMap->set(exec->globalData(), this, descriptor.value());
    372         else if (oldDescriptor.isAccessorDescriptor())
    373             entryInMap->set(exec->globalData(), this, jsUndefined());
    374         entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
    375         return;
    376     }
    377 
    378     if (descriptor.isAccessorDescriptor()) {
    379         JSObject* getter = 0;
    380         if (descriptor.getterPresent())
    381             getter = descriptor.getterObject();
    382         else if (oldDescriptor.isAccessorDescriptor())
    383             getter = oldDescriptor.getterObject();
    384         JSObject* setter = 0;
    385         if (descriptor.setterPresent())
    386             setter = descriptor.setterObject();
    387         else if (oldDescriptor.isAccessorDescriptor())
    388             setter = oldDescriptor.setterObject();
    389 
    390         GetterSetter* accessor = GetterSetter::create(exec);
    391         if (getter)
    392             accessor->setGetter(exec->globalData(), getter);
    393         if (setter)
    394             accessor->setSetter(exec->globalData(), setter);
    395 
    396         entryInMap->set(exec->globalData(), this, accessor);
    397         entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
    398         return;
    399     }
    400 
    401     ASSERT(descriptor.isGenericDescriptor());
    402     entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
    403 }
    404 
    405 // Defined in ES5.1 8.12.9
    406 bool JSArray::defineOwnNumericProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException)
    407 {
    408     ASSERT(index != 0xFFFFFFFF);
    409 
    410     if (!inSparseMode()) {
    411         // Fast case: we're putting a regular property to a regular array
    412         // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
    413         // – however if the property currently exists missing attributes will override from their current 'true'
    414         // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
    415         if (!descriptor.attributes()) {
    416             ASSERT(!descriptor.isAccessorDescriptor());
    417             return putDirectIndex(exec, index, descriptor.value(), throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
    418         }
    419 
    420         enterDictionaryMode(exec->globalData());
    421     }
    422 
    423     SparseArrayValueMap* map = m_sparseValueMap;
    424     ASSERT(map);
    425 
    426     // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
    427     SparseArrayValueMap::AddResult result = map->add(this, index);
    428     SparseArrayEntry* entryInMap = &result.iterator->second;
    429 
    430     // 2. Let extensible be the value of the [[Extensible]] internal property of O.
    431     // 3. If current is undefined and extensible is false, then Reject.
    432     // 4. If current is undefined and extensible is true, then
    433     if (result.isNewEntry) {
    434         if (!isExtensible()) {
    435             map->remove(result.iterator);
    436             return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
    437         }
    438 
    439         // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
    440         // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
    441         // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
    442         // created property is set to its default value.
    443         // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
    444         // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
    445         // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
    446         // is set to its default value.
    447         // 4.c. Return true.
    448 
    449         PropertyDescriptor defaults;
    450         entryInMap->setWithoutWriteBarrier(jsUndefined());
    451         entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
    452         entryInMap->get(defaults);
    453 
    454         putDescriptor(exec, entryInMap, descriptor, defaults);
    455         if (index >= m_storage->m_length)
    456             m_storage->m_length = index + 1;
    457         return true;
    458     }
    459 
    460     // 5. Return true, if every field in Desc is absent.
    461     // 6. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value as the corresponding field in current when compared using the SameValue algorithm (9.12).
    462     PropertyDescriptor current;
    463     entryInMap->get(current);
    464     if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
    465         return true;
    466 
    467     // 7. If the [[Configurable]] field of current is false then
    468     if (!current.configurable()) {
    469         // 7.a. Reject, if the [[Configurable]] field of Desc is true.
    470         if (descriptor.configurablePresent() && descriptor.configurable())
    471             return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
    472         // 7.b. Reject, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
    473         if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
    474             return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
    475     }
    476 
    477     // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
    478     if (!descriptor.isGenericDescriptor()) {
    479         // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
    480         if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
    481             // 9.a. Reject, if the [[Configurable]] field of current is false.
    482             if (!current.configurable())
    483                 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
    484             // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
    485             // data property to an accessor property. Preserve the existing values of the converted property‘s
    486             // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property‘s attributes to
    487             // their default values.
    488             // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
    489             // Preserve the existing values of the converted property‘s [[Configurable]] and [[Enumerable]]
    490             // attributes and set the rest of the property‘s attributes to their default values.
    491         } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
    492             // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
    493             // 10.a. If the [[Configurable]] field of current is false, then
    494             if (!current.configurable() && !current.writable()) {
    495                 // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
    496                 if (descriptor.writable())
    497                     return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
    498                 // 10.a.ii. If the [[Writable]] field of current is false, then
    499                 // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
    500                 if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
    501                     return reject(exec, throwException, "Attempting to change value of a readonly property.");
    502             }
    503             // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
    504         } else {
    505             ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
    506             // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
    507             if (!current.configurable()) {
    508                 // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
    509                 if (descriptor.setterPresent() && descriptor.setter() != current.setter())
    510                     return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
    511                 // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
    512                 if (descriptor.getterPresent() && descriptor.getter() != current.getter())
    513                     return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
    514             }
    515         }
    516     }
    517 
    518     // 12. For each attribute field of Desc that is present, set the correspondingly named attribute of the property named P of object O to the value of the field.
    519     putDescriptor(exec, entryInMap, descriptor, current);
    520     // 13. Return true.
    521     return true;
     68    return butterfly;
    52269}
    52370
     
    52875        return;
    52976
    530     enterDictionaryMode(exec->globalData());
    531 
    532     SparseArrayValueMap* map = m_sparseValueMap;
     77    enterDictionaryIndexingMode(exec->globalData());
     78
     79    SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
    53380    ASSERT(map);
    53481    map->setLengthIsReadOnly();
     
    631178        // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
    632179        // f. Return true.
    633         return array->defineOwnNumericProperty(exec, index, descriptor, throwException);
    634     }
    635 
    636     return JSObject::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
    637 }
    638 
    639 bool JSArray::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot)
    640 {
    641     JSArray* thisObject = jsCast<JSArray*>(cell);
    642     ArrayStorage* storage = thisObject->m_storage;
    643 
    644     if (i >= storage->m_length) {
    645         if (i > MAX_ARRAY_INDEX)
    646             return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
    647         return false;
    648     }
    649 
    650     if (i < thisObject->m_vectorLength) {
    651         JSValue value = storage->m_vector[i].get();
    652         if (value) {
    653             slot.setValue(value);
    654             return true;
    655         }
    656     } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
    657         SparseArrayValueMap::iterator it = map->find(i);
    658         if (it != map->notFound()) {
    659             it->second.get(slot);
    660             return true;
    661         }
    662     }
    663 
    664     return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
     180        return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
     181    }
     182
     183    return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
    665184}
    666185
     
    673192    }
    674193
    675     unsigned i = propertyName.asIndex();
    676     if (i != PropertyName::NotAnIndex)
    677         return JSArray::getOwnPropertySlotByIndex(thisObject, exec, i, slot);
    678 
    679194    return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
    680195}
     
    688203    }
    689204
    690     ArrayStorage* storage = thisObject->m_storage;
    691    
    692     unsigned i = propertyName.asIndex();
    693     if (i != PropertyName::NotAnIndex) {
    694         if (i >= storage->m_length)
    695             return false;
    696         if (i < thisObject->m_vectorLength) {
    697             WriteBarrier<Unknown>& value = storage->m_vector[i];
    698             if (value) {
    699                 descriptor.setDescriptor(value.get(), 0);
    700                 return true;
    701             }
    702         } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
    703             SparseArrayValueMap::iterator it = map->find(i);
    704             if (it != map->notFound()) {
    705                 it->second.get(descriptor);
    706                 return true;
    707             }
    708         }
    709     }
    710205    return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
    711206}
     
    715210{
    716211    JSArray* thisObject = jsCast<JSArray*>(cell);
    717     unsigned i = propertyName.asIndex();
    718     if (i != PropertyName::NotAnIndex) {
    719         putByIndex(thisObject, exec, i, value, slot.isStrictMode());
    720         return;
    721     }
    722212
    723213    if (propertyName == exec->propertyNames().length) {
     
    734224}
    735225
    736 void JSArray::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
     226bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
    737227{
    738228    JSArray* thisObject = jsCast<JSArray*>(cell);
    739     thisObject->checkConsistency();
    740 
    741     ArrayStorage* storage = thisObject->m_storage;
    742 
    743     // Fast case - store to the vector.
    744     if (i < thisObject->m_vectorLength) {
    745         WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
    746         unsigned length = storage->m_length;
    747 
    748         // Update m_length & m_numValuesInVector as necessary.
    749         if (i >= length) {
    750             length = i + 1;
    751             storage->m_length = length;
    752             ++storage->m_numValuesInVector;
    753         } else if (!valueSlot)
    754             ++storage->m_numValuesInVector;
    755 
    756         valueSlot.set(exec->globalData(), thisObject, value);
    757         thisObject->checkConsistency();
    758         return;
    759     }
    760 
    761     // Handle 2^32-1 - this is not an array index (see ES5.1 15.4), and is treated as a regular property.
    762     if (UNLIKELY(i > MAX_ARRAY_INDEX)) {
    763         PutPropertySlot slot(shouldThrow);
    764         thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, i), value, slot);
    765         return;
    766     }
    767 
    768     // For all other cases, call putByIndexBeyondVectorLength.
    769     thisObject->putByIndexBeyondVectorLength(exec, i, value, shouldThrow);
    770     thisObject->checkConsistency();
    771 }
    772 
    773 void JSArray::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
    774 {
    775     JSGlobalData& globalData = exec->globalData();
    776 
    777     // i should be a valid array index that is outside of the current vector.
    778     ASSERT(i >= m_vectorLength);
    779     ASSERT(i <= MAX_ARRAY_INDEX);
    780 
    781     ArrayStorage* storage = m_storage;
    782     SparseArrayValueMap* map = m_sparseValueMap;
    783 
    784     // First, handle cases where we don't currently have a sparse map.
    785     if (LIKELY(!map)) {
    786         // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
    787         ASSERT(isExtensible());
    788    
    789         // Update m_length if necessary.
    790         if (i >= storage->m_length)
    791             storage->m_length = i + 1;
    792 
    793         // Check that it is sensible to still be using a vector, and then try to grow the vector.
    794         if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {
    795             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
    796             storage = m_storage;
    797             storage->m_vector[i].set(globalData, this, value);
    798             ++storage->m_numValuesInVector;
    799             return;
    800         }
    801         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
    802         allocateSparseMap(exec->globalData());
    803         map = m_sparseValueMap;
    804         map->put(exec, this, i, value, shouldThrow);
    805         return;
    806     }
    807 
    808     // Update m_length if necessary.
    809     unsigned length = storage->m_length;
    810     if (i >= length) {
    811         // Prohibit growing the array if length is not writable.
    812         if (map->lengthIsReadOnly() || !isExtensible()) {
    813             if (shouldThrow)
    814                 throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
    815             return;
    816         }
    817         length = i + 1;
    818         storage->m_length = length;
    819     }
    820 
    821     // We are currently using a map - check whether we still want to be doing so.
    822     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
    823     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
    824     if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) {
    825         map->put(exec, this, i, value, shouldThrow);
    826         return;
    827     }
    828 
    829     // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
    830     storage = m_storage;
    831     storage->m_numValuesInVector = numValuesInArray;
    832 
    833     // Copy all values from the map into the vector, and delete the map.
    834     WriteBarrier<Unknown>* vector = storage->m_vector;
    835     SparseArrayValueMap::const_iterator end = map->end();
    836     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
    837         vector[it->first].set(globalData, this, it->second.getNonSparseMode());
    838     deallocateSparseMap();
    839 
    840     // Store the new property into the vector.
    841     WriteBarrier<Unknown>& valueSlot = vector[i];
    842     if (!valueSlot)
    843         ++storage->m_numValuesInVector;
    844     valueSlot.set(globalData, this, value);
    845 }
    846 
    847 bool JSArray::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, PutDirectIndexMode mode)
    848 {
    849     JSGlobalData& globalData = exec->globalData();
    850 
    851     // i should be a valid array index that is outside of the current vector.
    852     ASSERT(i >= m_vectorLength);
    853     ASSERT(i <= MAX_ARRAY_INDEX);
    854 
    855     ArrayStorage* storage = m_storage;
    856     SparseArrayValueMap* map = m_sparseValueMap;
    857 
    858     // First, handle cases where we don't currently have a sparse map.
    859     if (LIKELY(!map)) {
    860         // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
    861         ASSERT(isExtensible());
    862    
    863         // Update m_length if necessary.
    864         if (i >= storage->m_length)
    865             storage->m_length = i + 1;
    866 
    867         // Check that it is sensible to still be using a vector, and then try to grow the vector.
    868         if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {
    869             // success! - reread m_storage since it has likely been reallocated, and store to the vector.
    870             storage = m_storage;
    871             storage->m_vector[i].set(globalData, this, value);
    872             ++storage->m_numValuesInVector;
    873             return true;
    874         }
    875         // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
    876         allocateSparseMap(exec->globalData());
    877         map = m_sparseValueMap;
    878         return map->putDirect(exec, this, i, value, mode);
    879     }
    880 
    881     // Update m_length if necessary.
    882     unsigned length = storage->m_length;
    883     if (i >= length) {
    884         // Prohibit growing the array if length is not writable.
    885         if (mode != PutDirectIndexLikePutDirect) {
    886             if (map->lengthIsReadOnly())
    887                 return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);
    888             if (!isExtensible())
    889                 return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
    890         }
    891         length = i + 1;
    892         storage->m_length = length;
    893     }
    894 
    895     // We are currently using a map - check whether we still want to be doing so.
    896     // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
    897     unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
    898     if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length))
    899         return map->putDirect(exec, this, i, value, mode);
    900 
    901     // Reread m_storage afterincreaseVectorLength, update m_numValuesInVector.
    902     storage = m_storage;
    903     storage->m_numValuesInVector = numValuesInArray;
    904 
    905     // Copy all values from the map into the vector, and delete the map.
    906     WriteBarrier<Unknown>* vector = storage->m_vector;
    907     SparseArrayValueMap::const_iterator end = map->end();
    908     for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
    909         vector[it->first].set(globalData, this, it->second.getNonSparseMode());
    910     deallocateSparseMap();
    911 
    912     // Store the new property into the vector.
    913     WriteBarrier<Unknown>& valueSlot = vector[i];
    914     if (!valueSlot)
    915         ++storage->m_numValuesInVector;
    916     valueSlot.set(globalData, this, value);
    917     return true;
    918 }
    919 
    920 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
    921 {
    922     JSArray* thisObject = jsCast<JSArray*>(cell);
    923     unsigned i = propertyName.asIndex();
    924     if (i != PropertyName::NotAnIndex)
    925         return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);
    926229
    927230    if (propertyName == exec->propertyNames().length)
     
    929232
    930233    return JSObject::deleteProperty(thisObject, exec, propertyName);
    931 }
    932 
    933 bool JSArray::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
    934 {
    935     JSArray* thisObject = jsCast<JSArray*>(cell);
    936     thisObject->checkConsistency();
    937 
    938     if (i > MAX_ARRAY_INDEX)
    939         return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
    940 
    941     ArrayStorage* storage = thisObject->m_storage;
    942    
    943     if (i < thisObject->m_vectorLength) {
    944         WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
    945         if (valueSlot) {
    946             valueSlot.clear();
    947             --storage->m_numValuesInVector;
    948         }
    949     } else if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
    950         SparseArrayValueMap::iterator it = map->find(i);
    951         if (it != map->notFound()) {
    952             if (it->second.attributes & DontDelete)
    953                 return false;
    954             map->remove(it);
    955         }
    956     }
    957 
    958     thisObject->checkConsistency();
    959     return true;
    960234}
    961235
     
    967241}
    968242
    969 void JSArray::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
     243void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    970244{
    971245    JSArray* thisObject = jsCast<JSArray*>(object);
    972     // FIXME: Filling PropertyNameArray with an identifier for every integer
    973     // is incredibly inefficient for large arrays. We need a different approach,
    974     // which almost certainly means a different structure for PropertyNameArray.
    975 
    976     ArrayStorage* storage = thisObject->m_storage;
    977    
    978     unsigned usedVectorLength = min(storage->m_length, thisObject->m_vectorLength);
    979     for (unsigned i = 0; i < usedVectorLength; ++i) {
    980         if (storage->m_vector[i])
    981             propertyNames.add(Identifier::from(exec, i));
    982     }
    983 
    984     if (SparseArrayValueMap* map = thisObject->m_sparseValueMap) {
    985         Vector<unsigned> keys;
    986         keys.reserveCapacity(map->size());
    987        
    988         SparseArrayValueMap::const_iterator end = map->end();
    989         for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
    990             if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum))
    991                 keys.append(static_cast<unsigned>(it->first));
    992         }
    993 
    994         qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
    995         for (unsigned i = 0; i < keys.size(); ++i)
    996             propertyNames.add(Identifier::from(exec, keys[i]));
    997     }
    998246
    999247    if (mode == IncludeDontEnumProperties)
    1000248        propertyNames.add(exec->propertyNames().length);
    1001249
    1002     JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
    1003 }
    1004 
    1005 ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength)
    1006 {
    1007     ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
    1008 
    1009     unsigned increasedLength;
    1010     unsigned maxInitLength = min(m_storage->m_length, 100000U);
    1011 
    1012     if (desiredLength < maxInitLength)
    1013         increasedLength = maxInitLength;
    1014     else if (!m_vectorLength)
    1015         increasedLength = max(desiredLength, lastArraySize);
    1016     else {
    1017         // Mathematically equivalent to:
    1018         //   increasedLength = (newLength * 3 + 1) / 2;
    1019         // or:
    1020         //   increasedLength = (unsigned)ceil(newLength * 1.5));
    1021         // This form is not prone to internal overflow.
    1022         increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1);
    1023     }
    1024 
    1025     ASSERT(increasedLength >= desiredLength);
    1026 
    1027     lastArraySize = min(increasedLength, FIRST_VECTOR_GROW);
    1028 
    1029     return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
    1030 }
    1031 
    1032 bool JSArray::increaseVectorLength(JSGlobalData& globalData, unsigned newLength)
    1033 {
    1034     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
    1035     // to the vector. Callers have to account for that, because they can do it more efficiently.
    1036     if (newLength > MAX_STORAGE_VECTOR_LENGTH)
    1037         return false;
    1038 
    1039     ArrayStorage* storage = m_storage;
    1040 
    1041     unsigned vectorLength = m_vectorLength;
    1042     ASSERT(newLength > vectorLength);
    1043     unsigned newVectorLength = getNewVectorLength(newLength);
    1044 
    1045     // Fast case - there is no precapacity. In these cases a realloc makes sense.
    1046     if (LIKELY(!m_indexBias)) {
    1047         void* newStorage = storage->m_allocBase;
    1048         if (!globalData.heap.tryReallocateStorage(&newStorage, storageSize(vectorLength), storageSize(newVectorLength)))
    1049             return false;
    1050 
    1051         storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newStorage));
    1052         m_storage->m_allocBase = newStorage;
    1053         ASSERT(m_storage->m_allocBase);
    1054 
    1055         m_vectorLength = newVectorLength;
    1056        
    1057         return true;
    1058     }
    1059 
    1060     // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
    1061     unsigned newIndexBias = min(m_indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
    1062     // Calculate new stoarge capcity, allowing room for the pre-capacity.
    1063     unsigned newStorageCapacity = newVectorLength + newIndexBias;
    1064     void* newAllocBase = 0;
    1065     if (!globalData.heap.tryAllocateStorage(storageSize(newStorageCapacity), &newAllocBase))   
    1066         return false;
    1067     // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
    1068     ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias);
    1069 
    1070     m_vectorLength = newVectorLength;
    1071     m_indexBias = newIndexBias;
    1072     m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias);
    1073 
    1074     // Copy the ArrayStorage header & current contents of the vector.
    1075     memmove(m_storage, storage, storageSize(vectorLength));
    1076 
    1077     // Free the old allocation, update m_allocBase.
    1078     m_storage->m_allocBase = newAllocBase;
    1079 
    1080     return true;
     250    JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
    1081251}
    1082252
     
    1084254bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, unsigned count)
    1085255{
     256    ArrayStorage* storage = ensureArrayStorage(globalData);
     257    Butterfly* butterfly = storage->butterfly();
     258    unsigned propertyCapacity = structure()->outOfLineCapacity();
     259    unsigned propertySize = structure()->outOfLineSize();
     260
    1086261    // If not, we should have handled this on the fast path.
    1087     ASSERT(count > m_indexBias);
    1088 
    1089     ArrayStorage* storage = m_storage;
     262    ASSERT(count > storage->m_indexBias);
    1090263
    1091264    // Step 1:
     
    1096269    //  * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
    1097270
    1098     unsigned length = storage->m_length;
    1099     unsigned usedVectorLength = min(m_vectorLength, length);
     271    unsigned length = storage->length();
     272    unsigned usedVectorLength = min(storage->vectorLength(), length);
    1100273    ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
    1101274    // Check that required vector length is possible, in an overflow-safe fashion.
     
    1105278    ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
    1106279    // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
    1107     ASSERT(m_vectorLength <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - m_vectorLength) >= m_indexBias);
    1108     unsigned currentCapacity = m_vectorLength + m_indexBias;
     280    ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
     281    unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
    1109282    // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
    1110283    unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1);
     
    1117290    // If the current storage array is sufficiently large (but not too large!) then just keep using it.
    1118291    if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
    1119         newAllocBase = storage->m_allocBase;
     292        newAllocBase = butterfly->base(structure());
    1120293        newStorageCapacity = currentCapacity;
    1121294    } else {
    1122         if (!globalData.heap.tryAllocateStorage(storageSize(desiredCapacity), &newAllocBase))
     295        size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
     296        if (!globalData.heap.tryAllocateStorage(newSize, &newAllocBase))
    1123297            return false;
    1124298        newStorageCapacity = desiredCapacity;
     
    1133307    // vector with half the post-capacity it had previously.
    1134308    unsigned postCapacity = 0;
    1135     if (length < m_vectorLength) {
     309    if (length < storage->vectorLength()) {
    1136310        // Atomic decay, + the post-capacity cannot be greater than what is available.
    1137         postCapacity = min((m_vectorLength - length) >> 1, newStorageCapacity - requiredVectorLength);
     311        postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
    1138312        // If we're moving contents within the same allocation, the post-capacity is being reduced.
    1139         ASSERT(newAllocBase != storage->m_allocBase || postCapacity < m_vectorLength - length);
    1140     }
    1141 
    1142     m_vectorLength = requiredVectorLength + postCapacity;
    1143     m_indexBias = newStorageCapacity - m_vectorLength;
    1144     m_storage = reinterpret_cast_ptr<ArrayStorage*>(reinterpret_cast<WriteBarrier<Unknown>*>(newAllocBase) + m_indexBias);
    1145 
    1146     // Step 4:
    1147     // Copy array data / header into their new locations, clear post-capacity & free any old allocation.
    1148 
    1149     // If this is being moved within the existing buffer of memory, we are always shifting data
    1150     // to the right (since count > m_indexBias). As such this memmove cannot trample the header.
    1151     memmove(m_storage->m_vector + count, storage->m_vector, sizeof(WriteBarrier<Unknown>) * usedVectorLength);
    1152     memmove(m_storage, storage, storageSize(0));
    1153 
    1154     // Are we copying into a new allocation?
    1155     if (newAllocBase != m_storage->m_allocBase) {
    1156         // Free the old allocation, update m_allocBase.
    1157         m_storage->m_allocBase = newAllocBase;
    1158     }
     313        ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length);
     314    }
     315   
     316    unsigned newVectorLength = requiredVectorLength + postCapacity;
     317    unsigned newIndexBias = newStorageCapacity - newVectorLength;
     318
     319    Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity);
     320   
     321    memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
     322    memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
     323   
     324    newButterfly->arrayStorage()->setVectorLength(newVectorLength);
     325    newButterfly->arrayStorage()->m_indexBias = newIndexBias;
     326   
     327    m_butterfly = newButterfly;
    1159328
    1160329    return true;
     
    1163332bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
    1164333{
    1165     checkConsistency();
    1166 
    1167     ArrayStorage* storage = m_storage;
    1168     unsigned length = storage->m_length;
     334    checkIndexingConsistency();
     335
     336    ArrayStorage* storage = ensureArrayStorage(exec->globalData());
     337    unsigned length = storage->length();
    1169338
    1170339    // If the length is read only then we enter sparse mode, so should enter the following 'if'.
    1171     ASSERT(isLengthWritable() || m_sparseValueMap);
    1172 
    1173     if (SparseArrayValueMap* map = m_sparseValueMap) {
     340    ASSERT(isLengthWritable() || storage->m_sparseMap);
     341
     342    if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
    1174343        // Fail if the length is not writable.
    1175344        if (map->lengthIsReadOnly())
     
    1198367                    ASSERT(it != map->notFound());
    1199368                    if (it->second.attributes & DontDelete) {
    1200                         storage->m_length = index + 1;
     369                        storage->setLength(index + 1);
    1201370                        return reject(exec, throwException, "Unable to delete property.");
    1202371                    }
     
    1207376                    map->remove(keys[i]);
    1208377                if (map->isEmpty())
    1209                     deallocateSparseMap();
     378                    deallocateSparseIndexMap();
    1210379            }
    1211380        }
     
    1214383    if (newLength < length) {
    1215384        // Delete properties from the vector.
    1216         unsigned usedVectorLength = min(length, m_vectorLength);
     385        unsigned usedVectorLength = min(length, storage->vectorLength());
    1217386        for (unsigned i = newLength; i < usedVectorLength; ++i) {
    1218387            WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
     
    1223392    }
    1224393
    1225     storage->m_length = newLength;
    1226 
    1227     checkConsistency();
     394    storage->setLength(newLength);
     395
     396    checkIndexingConsistency();
    1228397    return true;
    1229398}
     
    1231400JSValue JSArray::pop(ExecState* exec)
    1232401{
    1233     checkConsistency();
    1234     ArrayStorage* storage = m_storage;
    1235    
    1236     unsigned length = storage->m_length;
    1237     if (!length) {
    1238         if (!isLengthWritable())
    1239             throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
     402    checkIndexingConsistency();
     403   
     404    switch (structure()->indexingType()) {
     405    case Array:
    1240406        return jsUndefined();
    1241     }
    1242 
    1243     unsigned index = length - 1;
    1244     if (index < m_vectorLength) {
    1245         WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
    1246         if (valueSlot) {
    1247             --storage->m_numValuesInVector;
    1248             JSValue element = valueSlot.get();
    1249             valueSlot.clear();
     407       
     408    case ArrayWithArrayStorage: {
     409        ArrayStorage* storage = m_butterfly->arrayStorage();
     410   
     411        unsigned length = storage->length();
     412        if (!length) {
     413            if (!isLengthWritable())
     414                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
     415            return jsUndefined();
     416        }
     417
     418        unsigned index = length - 1;
     419        if (index < storage->vectorLength()) {
     420            WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
     421            if (valueSlot) {
     422                --storage->m_numValuesInVector;
     423                JSValue element = valueSlot.get();
     424                valueSlot.clear();
    1250425           
    1251             ASSERT(isLengthWritable());
    1252             storage->m_length = index;
    1253             checkConsistency();
    1254             return element;
    1255         }
    1256     }
    1257 
    1258     // Let element be the result of calling the [[Get]] internal method of O with argument indx.
    1259     JSValue element = get(exec, index);
    1260     if (exec->hadException())
    1261         return jsUndefined();
    1262     // Call the [[Delete]] internal method of O with arguments indx and true.
    1263     if (!deletePropertyByIndex(this, exec, index)) {
    1264         throwTypeError(exec, ASCIILiteral("Unable to delete property."));
    1265         return jsUndefined();
    1266     }
    1267     // Call the [[Put]] internal method of O with arguments "length", indx, and true.
    1268     setLength(exec, index, true);
    1269     // Return element.
    1270     checkConsistency();
    1271     return element;
     426                ASSERT(isLengthWritable());
     427                storage->setLength(index);
     428                checkIndexingConsistency();
     429                return element;
     430            }
     431        }
     432
     433        // Let element be the result of calling the [[Get]] internal method of O with argument indx.
     434        JSValue element = get(exec, index);
     435        if (exec->hadException())
     436            return jsUndefined();
     437        // Call the [[Delete]] internal method of O with arguments indx and true.
     438        if (!deletePropertyByIndex(this, exec, index)) {
     439            throwTypeError(exec, "Unable to delete property.");
     440            return jsUndefined();
     441        }
     442        // Call the [[Put]] internal method of O with arguments "length", indx, and true.
     443        setLength(exec, index, true);
     444        // Return element.
     445        checkIndexingConsistency();
     446        return element;
     447    }
     448       
     449    default:
     450        ASSERT_NOT_REACHED();
     451        return JSValue();
     452    }
    1272453}
    1273454
     
    1277458void JSArray::push(ExecState* exec, JSValue value)
    1278459{
    1279     checkConsistency();
    1280     ArrayStorage* storage = m_storage;
    1281 
    1282     // Fast case - push within vector, always update m_length & m_numValuesInVector.
    1283     unsigned length = storage->m_length;
    1284     if (length < m_vectorLength) {
    1285         storage->m_vector[length].set(exec->globalData(), this, value);
    1286         storage->m_length = length + 1;
    1287         ++storage->m_numValuesInVector;
    1288         checkConsistency();
    1289         return;
    1290     }
    1291 
    1292     // Pushing to an array of length 2^32-1 stores the property, but throws a range error.
    1293     if (UNLIKELY(storage->m_length == 0xFFFFFFFFu)) {
    1294         methodTable()->putByIndex(this, exec, storage->m_length, value, true);
    1295         // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
    1296         if (!exec->hadException())
    1297             throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
    1298         return;
    1299     }
    1300 
    1301     // Handled the same as putIndex.
    1302     putByIndexBeyondVectorLength(exec, storage->m_length, value, true);
    1303     checkConsistency();
    1304 }
    1305 
    1306 bool JSArray::shiftCount(ExecState*, unsigned count)
     460    checkIndexingConsistency();
     461   
     462    switch (structure()->indexingType()) {
     463    case Array: {
     464        putByIndexBeyondVectorLengthWithArrayStorage(exec, 0, value, true, createInitialArrayStorage(exec->globalData()));
     465        break;
     466    }
     467       
     468    case ArrayWithArrayStorage: {
     469        ArrayStorage* storage = m_butterfly->arrayStorage();
     470
     471        // Fast case - push within vector, always update m_length & m_numValuesInVector.
     472        unsigned length = storage->length();
     473        if (length < storage->vectorLength()) {
     474            storage->m_vector[length].set(exec->globalData(), this, value);
     475            storage->setLength(length + 1);
     476            ++storage->m_numValuesInVector;
     477            checkIndexingConsistency();
     478            return;
     479        }
     480
     481        // Pushing to an array of length 2^32-1 stores the property, but throws a range error.
     482        if (UNLIKELY(storage->length() == 0xFFFFFFFFu)) {
     483            methodTable()->putByIndex(this, exec, storage->length(), value, true);
     484            // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
     485            if (!exec->hadException())
     486                throwError(exec, createRangeError(exec, "Invalid array length"));
     487            return;
     488        }
     489
     490        // Handled the same as putIndex.
     491        putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
     492        checkIndexingConsistency();
     493        break;
     494    }
     495       
     496    default:
     497        ASSERT_NOT_REACHED();
     498    }
     499}
     500
     501bool JSArray::shiftCount(ExecState* exec, unsigned count)
    1307502{
    1308503    ASSERT(count > 0);
    1309504   
    1310     ArrayStorage* storage = m_storage;
    1311    
    1312     unsigned oldLength = storage->m_length;
     505    ArrayStorage* storage = ensureArrayStorage(exec->globalData());
     506   
     507    unsigned oldLength = storage->length();
    1313508   
    1314509    // If the array contains holes or is otherwise in an abnormal state,
    1315510    // use the generic algorithm in ArrayPrototype.
    1316     if (oldLength != storage->m_numValuesInVector || inSparseMode())
     511    if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode())
    1317512        return false;
    1318513
     
    1321516   
    1322517    storage->m_numValuesInVector -= count;
    1323     storage->m_length -= count;
    1324    
    1325     if (m_vectorLength) {
    1326         count = min(m_vectorLength, (unsigned)count);
    1327        
    1328         m_vectorLength -= count;
    1329        
    1330         if (m_vectorLength) {
    1331             char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(WriteBarrier<Unknown>);
    1332             memmove(newBaseStorage, storage, storageSize(0));
    1333             m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);
    1334 
    1335             m_indexBias += count;
     518    storage->setLength(oldLength - count);
     519   
     520    unsigned vectorLength = storage->vectorLength();
     521    if (vectorLength) {
     522        count = min(vectorLength, (unsigned)count);
     523       
     524        vectorLength -= count;
     525        storage->setVectorLength(vectorLength);
     526       
     527        if (vectorLength) {
     528            m_butterfly = m_butterfly->shift(structure(), count);
     529            storage = m_butterfly->arrayStorage();
     530            storage->m_indexBias += count;
    1336531        }
    1337532    }
     
    1342537bool JSArray::unshiftCount(ExecState* exec, unsigned count)
    1343538{
    1344     ArrayStorage* storage = m_storage;
    1345     unsigned length = storage->m_length;
     539    ArrayStorage* storage = ensureArrayStorage(exec->globalData());
     540    unsigned length = storage->length();
    1346541
    1347542    // If the array contains holes or is otherwise in an abnormal state,
    1348543    // use the generic algorithm in ArrayPrototype.
    1349     if (length != storage->m_numValuesInVector || inSparseMode())
     544    if (length != storage->m_numValuesInVector || storage->inSparseMode())
    1350545        return false;
    1351546
    1352     if (m_indexBias >= count) {
    1353         m_indexBias -= count;
    1354         char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(WriteBarrier<Unknown>);
    1355         memmove(newBaseStorage, storage, storageSize(0));
    1356         m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);
    1357         m_vectorLength += count;
     547    if (storage->m_indexBias >= count) {
     548        m_butterfly = storage->butterfly()->unshift(structure(), count);
     549        storage = m_butterfly->arrayStorage();
     550        storage->m_indexBias -= count;
     551        storage->setVectorLength(storage->vectorLength() + count);
    1358552    } else if (!unshiftCountSlowCase(exec->globalData(), count)) {
    1359553        throwOutOfMemoryError(exec);
     
    1361555    }
    1362556
    1363     WriteBarrier<Unknown>* vector = m_storage->m_vector;
     557    WriteBarrier<Unknown>* vector = storage->m_vector;
    1364558    for (unsigned i = 0; i < count; i++)
    1365559        vector[i].clear();
     
    1367561}
    1368562
    1369 void JSArray::visitChildren(JSCell* cell, SlotVisitor& visitor)
    1370 {
    1371     JSArray* thisObject = jsCast<JSArray*>(cell);
    1372     ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
    1373     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
    1374     ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
    1375 
    1376     JSNonFinalObject::visitChildren(thisObject, visitor);
    1377 
    1378     if (thisObject->m_storage) {
    1379         MARK_LOG_MESSAGE1("[%u]: ", thisObject->length());
    1380 
    1381         ArrayStorage* storage = thisObject->m_storage;
    1382         void* baseStorage = storage->m_allocBase;
    1383 
    1384         visitor.copyAndAppend(reinterpret_cast<void**>(&baseStorage), storageSize(thisObject->m_vectorLength + thisObject->m_indexBias), storage->m_vector->slot(), thisObject->m_vectorLength);
    1385 
    1386         if (baseStorage != thisObject->m_storage->m_allocBase) {
    1387             thisObject->m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + sizeof(JSValue) * thisObject->m_indexBias);
    1388             thisObject->m_storage->m_allocBase = baseStorage;
    1389             ASSERT(thisObject->m_storage->m_allocBase);
    1390         }
    1391     }
    1392 
    1393     if (SparseArrayValueMap* map = thisObject->m_sparseValueMap)
    1394         map->visitChildren(visitor);
    1395 }
    1396 
    1397563static int compareNumbersForQSort(const void* a, const void* b)
    1398564{
     
    1411577void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
    1412578{
    1413     ASSERT(!inSparseMode());
    1414 
    1415     ArrayStorage* storage = m_storage;
    1416 
    1417     unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
    1418     if (m_sparseValueMap) {
    1419         throwOutOfMemoryError(exec);
    1420         return;
    1421     }
    1422 
    1423     if (!lengthNotIncludingUndefined)
    1424         return;
    1425        
    1426     bool allValuesAreNumbers = true;
    1427     size_t size = storage->m_numValuesInVector;
    1428     for (size_t i = 0; i < size; ++i) {
    1429         if (!storage->m_vector[i].isNumber()) {
    1430             allValuesAreNumbers = false;
    1431             break;
    1432         }
    1433     }
    1434 
    1435     if (!allValuesAreNumbers)
    1436         return sort(exec, compareFunction, callType, callData);
    1437 
    1438     // For numeric comparison, which is fast, qsort is faster than mergesort. We
    1439     // also don't require mergesort's stability, since there's no user visible
    1440     // side-effect from swapping the order of equal primitive values.
    1441     qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort);
    1442 
    1443     checkConsistency(SortConsistencyCheck);
     579    ASSERT(!inSparseIndexingMode());
     580
     581    switch (structure()->indexingType()) {
     582    case Array:
     583        return;
     584       
     585    case ArrayWithArrayStorage: {
     586        unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
     587        ArrayStorage* storage = m_butterfly->arrayStorage();
     588       
     589        if (storage->m_sparseMap.get()) {
     590            throwOutOfMemoryError(exec);
     591            return;
     592        }
     593       
     594        if (!lengthNotIncludingUndefined)
     595            return;
     596       
     597        bool allValuesAreNumbers = true;
     598        size_t size = storage->m_numValuesInVector;
     599        for (size_t i = 0; i < size; ++i) {
     600            if (!storage->m_vector[i].isNumber()) {
     601                allValuesAreNumbers = false;
     602                break;
     603            }
     604        }
     605       
     606        if (!allValuesAreNumbers)
     607            return sort(exec, compareFunction, callType, callData);
     608       
     609        // For numeric comparison, which is fast, qsort is faster than mergesort. We
     610        // also don't require mergesort's stability, since there's no user visible
     611        // side-effect from swapping the order of equal primitive values.
     612        qsort(storage->m_vector, size, sizeof(WriteBarrier<Unknown>), compareNumbersForQSort);
     613       
     614        checkIndexingConsistency(SortConsistencyCheck);
     615        return;
     616    }
     617       
     618    default:
     619        ASSERT_NOT_REACHED();
     620    }
    1444621}
    1445622
    1446623void JSArray::sort(ExecState* exec)
    1447624{
    1448     ASSERT(!inSparseMode());
    1449 
    1450     unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
    1451     if (m_sparseValueMap) {
    1452         throwOutOfMemoryError(exec);
    1453         return;
    1454     }
    1455 
    1456     if (!lengthNotIncludingUndefined)
    1457         return;
    1458 
    1459     // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
    1460     // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
    1461     // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
    1462     // random or otherwise changing results, effectively making compare function inconsistent.
    1463 
    1464     Vector<ValueStringPair> values(lengthNotIncludingUndefined);
    1465     if (!values.begin()) {
    1466         throwOutOfMemoryError(exec);
    1467         return;
    1468     }
    1469    
    1470     Heap::heap(this)->pushTempSortVector(&values);
    1471 
    1472     bool isSortingPrimitiveValues = true;
    1473     for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
    1474         JSValue value = m_storage->m_vector[i].get();
    1475         ASSERT(!value.isUndefined());
    1476         values[i].first = value;
    1477         isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
    1478     }
    1479 
    1480     // FIXME: The following loop continues to call toString on subsequent values even after
    1481     // a toString call raises an exception.
    1482 
    1483     for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
    1484         values[i].second = values[i].first.toWTFStringInline(exec);
    1485 
    1486     if (exec->hadException()) {
     625    ASSERT(!inSparseIndexingMode());
     626   
     627    switch (structure()->indexingType()) {
     628    case Array:
     629        return;
     630       
     631    case ArrayWithArrayStorage: {
     632        unsigned lengthNotIncludingUndefined = compactForSorting(exec->globalData());
     633        ArrayStorage* storage = m_butterfly->arrayStorage();
     634        if (storage->m_sparseMap.get()) {
     635            throwOutOfMemoryError(exec);
     636            return;
     637        }
     638       
     639        if (!lengthNotIncludingUndefined)
     640            return;
     641       
     642        // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
     643        // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
     644        // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
     645        // random or otherwise changing results, effectively making compare function inconsistent.
     646       
     647        Vector<ValueStringPair> values(lengthNotIncludingUndefined);
     648        if (!values.begin()) {
     649            throwOutOfMemoryError(exec);
     650            return;
     651        }
     652       
     653        Heap::heap(this)->pushTempSortVector(&values);
     654       
     655        bool isSortingPrimitiveValues = true;
     656        for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
     657            JSValue value = storage->m_vector[i].get();
     658            ASSERT(!value.isUndefined());
     659            values[i].first = value;
     660            isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
     661        }
     662       
     663        // FIXME: The following loop continues to call toString on subsequent values even after
     664        // a toString call raises an exception.
     665       
     666        for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
     667            values[i].second = values[i].first.toWTFStringInline(exec);
     668       
     669        if (exec->hadException()) {
     670            Heap::heap(this)->popTempSortVector(&values);
     671            return;
     672        }
     673       
     674        // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
     675        // than O(N log N).
     676       
     677#if HAVE(MERGESORT)
     678        if (isSortingPrimitiveValues)
     679            qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
     680        else
     681            mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
     682#else
     683        // FIXME: The qsort library function is likely to not be a stable sort.
     684        // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
     685        qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
     686#endif
     687       
     688        // If the toString function changed the length of the array or vector storage,
     689        // increase the length to handle the orignal number of actual values.
     690        if (storage->vectorLength() < lengthNotIncludingUndefined) {
     691            increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined);
     692            storage = m_butterfly->arrayStorage();
     693        }
     694        if (storage->length() < lengthNotIncludingUndefined)
     695            storage->setLength(lengthNotIncludingUndefined);
     696       
     697        JSGlobalData& globalData = exec->globalData();
     698        for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
     699            storage->m_vector[i].set(globalData, this, values[i].first);
     700       
    1487701        Heap::heap(this)->popTempSortVector(&values);
    1488         return;
    1489     }
    1490 
    1491     // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
    1492     // than O(N log N).
    1493 
    1494 #if HAVE(MERGESORT)
    1495     if (isSortingPrimitiveValues)
    1496         qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
    1497     else
    1498         mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
    1499 #else
    1500     // FIXME: The qsort library function is likely to not be a stable sort.
    1501     // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
    1502     qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
    1503 #endif
    1504 
    1505     // If the toString function changed the length of the array or vector storage,
    1506     // increase the length to handle the orignal number of actual values.
    1507     if (m_vectorLength < lengthNotIncludingUndefined)
    1508         increaseVectorLength(exec->globalData(), lengthNotIncludingUndefined);
    1509     if (m_storage->m_length < lengthNotIncludingUndefined)
    1510         m_storage->m_length = lengthNotIncludingUndefined;
    1511 
    1512     JSGlobalData& globalData = exec->globalData();
    1513     for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
    1514         m_storage->m_vector[i].set(globalData, this, values[i].first);
    1515 
    1516     Heap::heap(this)->popTempSortVector(&values);
    1517    
    1518     checkConsistency(SortConsistencyCheck);
     702       
     703        checkIndexingConsistency(SortConsistencyCheck);
     704        return;
     705    }
     706       
     707    default:
     708        ASSERT_NOT_REACHED();
     709    }
    1519710}
    1520711
     
    1598789void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
    1599790{
    1600     ASSERT(!inSparseMode());
    1601 
    1602     checkConsistency();
    1603 
    1604     // FIXME: This ignores exceptions raised in the compare function or in toNumber.
    1605 
    1606     // The maximum tree depth is compiled in - but the caller is clearly up to no good
    1607     // if a larger array is passed.
    1608     ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
    1609     if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
    1610         return;
    1611 
    1612     unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
    1613     unsigned nodeCount = usedVectorLength + (m_sparseValueMap ? m_sparseValueMap->size() : 0);
    1614 
    1615     if (!nodeCount)
    1616         return;
    1617 
    1618     AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
    1619     tree.abstractor().m_exec = exec;
    1620     tree.abstractor().m_compareFunction = compareFunction;
    1621     tree.abstractor().m_compareCallType = callType;
    1622     tree.abstractor().m_compareCallData = &callData;
    1623     tree.abstractor().m_nodes.grow(nodeCount);
    1624 
    1625     if (callType == CallTypeJS)
    1626         tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
    1627 
    1628     if (!tree.abstractor().m_nodes.begin()) {
    1629         throwOutOfMemoryError(exec);
    1630         return;
    1631     }
    1632 
    1633     // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
    1634     // right out from under us while we're building the tree here.
    1635 
    1636     unsigned numDefined = 0;
    1637     unsigned numUndefined = 0;
    1638 
    1639     // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
    1640     for (; numDefined < usedVectorLength; ++numDefined) {
    1641         JSValue v = m_storage->m_vector[numDefined].get();
    1642         if (!v || v.isUndefined())
    1643             break;
    1644         tree.abstractor().m_nodes[numDefined].value = v;
    1645         tree.insert(numDefined);
    1646     }
    1647     for (unsigned i = numDefined; i < usedVectorLength; ++i) {
    1648         JSValue v = m_storage->m_vector[i].get();
    1649         if (v) {
    1650             if (v.isUndefined())
    1651                 ++numUndefined;
    1652             else {
    1653                 tree.abstractor().m_nodes[numDefined].value = v;
     791    ASSERT(!inSparseIndexingMode());
     792   
     793    switch (structure()->indexingType()) {
     794    case Array:
     795        return;
     796       
     797    case ArrayWithArrayStorage: {
     798        ArrayStorage* storage = m_butterfly->arrayStorage();
     799        checkIndexingConsistency();
     800       
     801        // FIXME: This ignores exceptions raised in the compare function or in toNumber.
     802       
     803        // The maximum tree depth is compiled in - but the caller is clearly up to no good
     804        // if a larger array is passed.
     805        ASSERT(storage->length() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
     806        if (storage->length() > static_cast<unsigned>(std::numeric_limits<int>::max()))
     807            return;
     808       
     809        unsigned usedVectorLength = min(storage->length(), storage->vectorLength());
     810        unsigned nodeCount = usedVectorLength + (storage->m_sparseMap ? storage->m_sparseMap->size() : 0);
     811       
     812        if (!nodeCount)
     813            return;
     814       
     815        AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
     816        tree.abstractor().m_exec = exec;
     817        tree.abstractor().m_compareFunction = compareFunction;
     818        tree.abstractor().m_compareCallType = callType;
     819        tree.abstractor().m_compareCallData = &callData;
     820        tree.abstractor().m_nodes.grow(nodeCount);
     821       
     822        if (callType == CallTypeJS)
     823            tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
     824       
     825        if (!tree.abstractor().m_nodes.begin()) {
     826            throwOutOfMemoryError(exec);
     827            return;
     828        }
     829       
     830        // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
     831        // right out from under us while we're building the tree here.
     832       
     833        unsigned numDefined = 0;
     834        unsigned numUndefined = 0;
     835       
     836        // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
     837        for (; numDefined < usedVectorLength; ++numDefined) {
     838            JSValue v = storage->m_vector[numDefined].get();
     839            if (!v || v.isUndefined())
     840                break;
     841            tree.abstractor().m_nodes[numDefined].value = v;
     842            tree.insert(numDefined);
     843        }
     844        for (unsigned i = numDefined; i < usedVectorLength; ++i) {
     845            JSValue v = storage->m_vector[i].get();
     846            if (v) {
     847                if (v.isUndefined())
     848                    ++numUndefined;
     849                else {
     850                    tree.abstractor().m_nodes[numDefined].value = v;
     851                    tree.insert(numDefined);
     852                    ++numDefined;
     853                }
     854            }
     855        }
     856       
     857        unsigned newUsedVectorLength = numDefined + numUndefined;
     858       
     859        if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
     860            newUsedVectorLength += map->size();
     861            if (newUsedVectorLength > storage->vectorLength()) {
     862                // Check that it is possible to allocate an array large enough to hold all the entries.
     863                if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) {
     864                    throwOutOfMemoryError(exec);
     865                    return;
     866                }
     867                storage = m_butterfly->arrayStorage();
     868            }
     869           
     870            SparseArrayValueMap::const_iterator end = map->end();
     871            for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
     872                tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode();
    1654873                tree.insert(numDefined);
    1655874                ++numDefined;
    1656875            }
    1657         }
    1658     }
    1659 
    1660     unsigned newUsedVectorLength = numDefined + numUndefined;
    1661 
    1662     if (SparseArrayValueMap* map = m_sparseValueMap) {
    1663         newUsedVectorLength += map->size();
    1664         if (newUsedVectorLength > m_vectorLength) {
    1665             // Check that it is possible to allocate an array large enough to hold all the entries.
    1666             if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(exec->globalData(), newUsedVectorLength)) {
    1667                 throwOutOfMemoryError(exec);
    1668                 return;
     876           
     877            deallocateSparseIndexMap();
     878        }
     879       
     880        ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
     881       
     882        // FIXME: If the compare function changed the length of the array, the following might be
     883        // modifying the vector incorrectly.
     884       
     885        // Copy the values back into m_storage.
     886        AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
     887        iter.start_iter_least(tree);
     888        JSGlobalData& globalData = exec->globalData();
     889        for (unsigned i = 0; i < numDefined; ++i) {
     890            storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
     891            ++iter;
     892        }
     893       
     894        // Put undefined values back in.
     895        for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
     896            storage->m_vector[i].setUndefined();
     897       
     898        // Ensure that unused values in the vector are zeroed out.
     899        for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
     900            storage->m_vector[i].clear();
     901       
     902        storage->m_numValuesInVector = newUsedVectorLength;
     903       
     904        checkIndexingConsistency(SortConsistencyCheck);
     905        return;
     906    }
     907       
     908    default:
     909        ASSERT_NOT_REACHED();
     910    }
     911}
     912
     913void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
     914{
     915    switch (structure()->indexingType()) {
     916    case Array:
     917        return;
     918   
     919    case ArrayWithArrayStorage: {
     920        ArrayStorage* storage = m_butterfly->arrayStorage();
     921       
     922        WriteBarrier<Unknown>* vector = storage->m_vector;
     923        unsigned vectorEnd = min(storage->length(), storage->vectorLength());
     924        unsigned i = 0;
     925        for (; i < vectorEnd; ++i) {
     926            WriteBarrier<Unknown>& v = vector[i];
     927            if (!v)
     928                break;
     929            args.append(v.get());
     930        }
     931       
     932        for (; i < storage->length(); ++i)
     933            args.append(get(exec, i));
     934        return;
     935    }
     936       
     937    default:
     938        ASSERT_NOT_REACHED();
     939    }
     940}
     941
     942void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
     943{
     944    ASSERT(length == this->length());
     945    switch (structure()->indexingType()) {
     946    case Array:
     947        return;
     948       
     949    case ArrayWithArrayStorage: {
     950        ArrayStorage* storage = m_butterfly->arrayStorage();
     951        unsigned i = 0;
     952        WriteBarrier<Unknown>* vector = storage->m_vector;
     953        unsigned vectorEnd = min(length, storage->vectorLength());
     954        for (; i < vectorEnd; ++i) {
     955            WriteBarrier<Unknown>& v = vector[i];
     956            if (!v)
     957                break;
     958            callFrame->setArgument(i, v.get());
     959        }
     960       
     961        for (; i < length; ++i)
     962            callFrame->setArgument(i, get(exec, i));
     963        return;
     964    }
     965       
     966    default:
     967        ASSERT_NOT_REACHED();
     968    }
     969}
     970
     971unsigned JSArray::compactForSorting(JSGlobalData& globalData)
     972{
     973    ASSERT(!inSparseIndexingMode());
     974
     975    checkIndexingConsistency();
     976   
     977    switch (structure()->indexingType()) {
     978    case Array:
     979        return 0;
     980
     981    case ArrayWithArrayStorage: {
     982        ArrayStorage* storage = m_butterfly->arrayStorage();
     983       
     984        unsigned usedVectorLength = min(storage->length(), storage->vectorLength());
     985       
     986        unsigned numDefined = 0;
     987        unsigned numUndefined = 0;
     988       
     989        for (; numDefined < usedVectorLength; ++numDefined) {
     990            JSValue v = storage->m_vector[numDefined].get();
     991            if (!v || v.isUndefined())
     992                break;
     993        }
     994       
     995        for (unsigned i = numDefined; i < usedVectorLength; ++i) {
     996            JSValue v = storage->m_vector[i].get();
     997            if (v) {
     998                if (v.isUndefined())
     999                    ++numUndefined;
     1000                else
     1001                    storage->m_vector[numDefined++].setWithoutWriteBarrier(v);
    16691002            }
    16701003        }
    1671 
    1672         SparseArrayValueMap::const_iterator end = map->end();
    1673         for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
    1674             tree.abstractor().m_nodes[numDefined].value = it->second.getNonSparseMode();
    1675             tree.insert(numDefined);
    1676             ++numDefined;
    1677         }
    1678 
    1679         deallocateSparseMap();
    1680     }
    1681 
    1682     ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
    1683 
    1684     // FIXME: If the compare function changed the length of the array, the following might be
    1685     // modifying the vector incorrectly.
    1686 
    1687     // Copy the values back into m_storage.
    1688     AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
    1689     iter.start_iter_least(tree);
    1690     JSGlobalData& globalData = exec->globalData();
    1691     for (unsigned i = 0; i < numDefined; ++i) {
    1692         m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
    1693         ++iter;
    1694     }
    1695 
    1696     // Put undefined values back in.
    1697     for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
    1698         m_storage->m_vector[i].setUndefined();
    1699 
    1700     // Ensure that unused values in the vector are zeroed out.
    1701     for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
    1702         m_storage->m_vector[i].clear();
    1703 
    1704     m_storage->m_numValuesInVector = newUsedVectorLength;
    1705 
    1706     checkConsistency(SortConsistencyCheck);
    1707 }
    1708 
    1709 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
    1710 {
    1711     ArrayStorage* storage = m_storage;
    1712 
    1713     WriteBarrier<Unknown>* vector = storage->m_vector;
    1714     unsigned vectorEnd = min(storage->m_length, m_vectorLength);
    1715     unsigned i = 0;
    1716     for (; i < vectorEnd; ++i) {
    1717         WriteBarrier<Unknown>& v = vector[i];
    1718         if (!v)
    1719             break;
    1720         args.append(v.get());
    1721     }
    1722 
    1723     for (; i < storage->m_length; ++i)
    1724         args.append(get(exec, i));
    1725 }
    1726 
    1727 void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
    1728 {
    1729     ASSERT(length == this->length());
    1730     UNUSED_PARAM(length);
    1731     unsigned i = 0;
    1732     WriteBarrier<Unknown>* vector = m_storage->m_vector;
    1733     unsigned vectorEnd = min(length, m_vectorLength);
    1734     for (; i < vectorEnd; ++i) {
    1735         WriteBarrier<Unknown>& v = vector[i];
    1736         if (!v)
    1737             break;
    1738         callFrame->setArgument(i, v.get());
    1739     }
    1740 
    1741     for (; i < length; ++i)
    1742         callFrame->setArgument(i, get(exec, i));
    1743 }
    1744 
    1745 unsigned JSArray::compactForSorting(JSGlobalData& globalData)
    1746 {
    1747     ASSERT(!inSparseMode());
    1748 
    1749     checkConsistency();
    1750 
    1751     ArrayStorage* storage = m_storage;
    1752 
    1753     unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
    1754 
    1755     unsigned numDefined = 0;
    1756     unsigned numUndefined = 0;
    1757 
    1758     for (; numDefined < usedVectorLength; ++numDefined) {
    1759         JSValue v = storage->m_vector[numDefined].get();
    1760         if (!v || v.isUndefined())
    1761             break;
    1762     }
    1763 
    1764     for (unsigned i = numDefined; i < usedVectorLength; ++i) {
    1765         JSValue v = storage->m_vector[i].get();
    1766         if (v) {
    1767             if (v.isUndefined())
    1768                 ++numUndefined;
    1769             else
    1770                 storage->m_vector[numDefined++].setWithoutWriteBarrier(v);
    1771         }
    1772     }
    1773 
    1774     unsigned newUsedVectorLength = numDefined + numUndefined;
    1775 
    1776     if (SparseArrayValueMap* map = m_sparseValueMap) {
    1777         newUsedVectorLength += map->size();
    1778         if (newUsedVectorLength > m_vectorLength) {
    1779             // Check that it is possible to allocate an array large enough to hold all the entries - if not,
    1780             // exception is thrown by caller.
    1781             if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength))
    1782                 return 0;
    1783 
    1784             storage = m_storage;
    1785         }
    1786 
    1787         SparseArrayValueMap::const_iterator end = map->end();
    1788         for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
    1789             storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode());
    1790 
    1791         deallocateSparseMap();
    1792     }
    1793 
    1794     for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
    1795         storage->m_vector[i].setUndefined();
    1796     for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
    1797         storage->m_vector[i].clear();
    1798 
    1799     storage->m_numValuesInVector = newUsedVectorLength;
    1800 
    1801     checkConsistency(SortConsistencyCheck);
    1802 
    1803     return numDefined;
    1804 }
    1805 
    1806 #if CHECK_ARRAY_CONSISTENCY
    1807 
    1808 void JSArray::checkConsistency(ConsistencyCheckType type)
    1809 {
    1810     ArrayStorage* storage = m_storage;
    1811 
    1812     ASSERT(!storage->m_inCompactInitialization);
    1813 
    1814     ASSERT(storage);
    1815     if (type == SortConsistencyCheck)
    1816         ASSERT(!m_sparseValueMap);
    1817 
    1818     unsigned numValuesInVector = 0;
    1819     for (unsigned i = 0; i < m_vectorLength; ++i) {
    1820         if (JSValue value = storage->m_vector[i].get()) {
    1821             ASSERT(i < storage->m_length);
    1822             if (type != DestructorConsistencyCheck)
    1823                 value.isUndefined(); // Likely to crash if the object was deallocated.
    1824             ++numValuesInVector;
    1825         } else {
    1826             if (type == SortConsistencyCheck)
    1827                 ASSERT(i >= storage->m_numValuesInVector);
    1828         }
    1829     }
    1830     ASSERT(numValuesInVector == storage->m_numValuesInVector);
    1831     ASSERT(numValuesInVector <= storage->m_length);
    1832 
    1833     if (m_sparseValueMap) {
    1834         SparseArrayValueMap::const_iterator end = m_sparseValueMap->end();
    1835         for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) {
    1836             unsigned index = it->first;
    1837             ASSERT(index < storage->m_length);
    1838             ASSERT(index >= m_vectorLength);
    1839             ASSERT(index <= MAX_ARRAY_INDEX);
    1840             ASSERT(it->second);
    1841             if (type != DestructorConsistencyCheck)
    1842                 it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.
    1843         }
    1844     }
    1845 }
    1846 
    1847 #endif
     1004       
     1005        unsigned newUsedVectorLength = numDefined + numUndefined;
     1006       
     1007        if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
     1008            newUsedVectorLength += map->size();
     1009            if (newUsedVectorLength > storage->vectorLength()) {
     1010                // Check that it is possible to allocate an array large enough to hold all the entries - if not,
     1011                // exception is thrown by caller.
     1012                if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(globalData, newUsedVectorLength))
     1013                    return 0;
     1014               
     1015                storage = m_butterfly->arrayStorage();
     1016            }
     1017           
     1018            SparseArrayValueMap::const_iterator end = map->end();
     1019            for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
     1020                storage->m_vector[numDefined++].setWithoutWriteBarrier(it->second.getNonSparseMode());
     1021           
     1022            deallocateSparseIndexMap();
     1023        }
     1024       
     1025        for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
     1026            storage->m_vector[i].setUndefined();
     1027        for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
     1028            storage->m_vector[i].clear();
     1029       
     1030        storage->m_numValuesInVector = newUsedVectorLength;
     1031       
     1032        checkIndexingConsistency(SortConsistencyCheck);
     1033       
     1034        return numDefined;
     1035    }
     1036       
     1037    default:
     1038        ASSERT_NOT_REACHED();
     1039        return 0;
     1040    }
     1041}
     1042
    18481043
    18491044} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.