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.
Location:
trunk/Source/JavaScriptCore/runtime
Files:
13 added
1 deleted
39 edited

Legend:

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

    r127191 r128400  
    256256    }
    257257
    258     return JSObject::deleteProperty(thisObject, exec, Identifier(exec, String::number(i)));
     258    return JSObject::deletePropertyByIndex(thisObject, exec, i);
    259259}
    260260
     
    309309        PropertySlot slot;
    310310        if ((!thisObject->d->deletedArguments || !thisObject->d->deletedArguments[i]) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot))
    311             object->putDirect(exec->globalData(), propertyName, thisObject->argument(i).get(), 0);
     311            object->putDirectMayBeIndex(exec, propertyName, thisObject->argument(i).get());
    312312        if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
    313313            return false;
  • trunk/Source/JavaScriptCore/runtime/ArrayConstructor.cpp

    r127505 r128400  
    2626
    2727#include "ArrayPrototype.h"
     28#include "ButterflyInlineMethods.h"
     29#include "CopiedSpaceInlineMethods.h"
    2830#include "Error.h"
    2931#include "ExceptionHelpers.h"
  • trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp

    r127505 r128400  
    2525#include "ArrayPrototype.h"
    2626
     27#include "ButterflyInlineMethods.h"
    2728#include "CachedCall.h"
    2829#include "CodeBlock.h"
     30#include "CopiedSpaceInlineMethods.h"
    2931#include "Interpreter.h"
    3032#include "JIT.h"
     
    115117*/
    116118
     119ArrayPrototype* ArrayPrototype::create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
     120{
     121    Butterfly* butterfly = createArrayButterfly(exec->globalData(), 0);
     122    ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure, butterfly);
     123    prototype->finishCreation(globalObject);
     124    return prototype;
     125}
     126
    117127// ECMA 15.4.4
    118 ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure)
    119     : JSArray(globalObject->globalData(), structure)
     128ArrayPrototype::ArrayPrototype(JSGlobalObject* globalObject, Structure* structure, Butterfly* butterfly)
     129    : JSArray(globalObject->globalData(), structure, butterfly)
    120130{
    121131}
     
    293303    for (unsigned k = 0; k < length; k++) {
    294304        JSValue element;
    295         if (thisObj->canGetIndex(k))
    296             element = thisObj->getIndex(k);
     305        if (thisObj->canGetIndexQuickly(k))
     306            element = thisObj->getIndexQuickly(k);
    297307        else
    298308            element = thisObj->get(exec, k);
     
    414424
    415425        for (; k < length; k++) {
    416             if (!array->canGetIndex(k))
     426            if (!array->canGetIndexQuickly(k))
    417427                break;
    418428
    419             JSValue element = array->getIndex(k);
     429            JSValue element = array->getIndexQuickly(k);
    420430            if (!element.isUndefinedOrNull())
    421431                stringJoiner.append(element.toWTFStringInline(exec));
     
    629639    CallType callType = getCallData(function, callData);
    630640
    631     if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseMode()) {
     641    if (thisObj->classInfo() == &JSArray::s_info && !asArray(thisObj)->inSparseIndexingMode()) {
    632642        if (isNumericCompareFunction(exec, callType, callData))
    633643            asArray(thisObj)->sortNumeric(exec, function, callType, callData);
     
    789799        CachedCall cachedCall(exec, f, 3);
    790800        for (; k < length && !exec->hadException(); ++k) {
    791             if (!array->canGetIndex(k))
     801            if (!array->canGetIndexQuickly(k))
    792802                break;
    793             JSValue v = array->getIndex(k);
     803            JSValue v = array->getIndexQuickly(k);
    794804            cachedCall.setThis(applyThis);
    795805            cachedCall.setArgument(0, v);
     
    847857        CachedCall cachedCall(exec, f, 3);
    848858        for (; k < length && !exec->hadException(); ++k) {
    849             if (UNLIKELY(!array->canGetIndex(k)))
     859            if (UNLIKELY(!array->canGetIndexQuickly(k)))
    850860                break;
    851861
    852862            cachedCall.setThis(applyThis);
    853             cachedCall.setArgument(0, array->getIndex(k));
     863            cachedCall.setArgument(0, array->getIndexQuickly(k));
    854864            cachedCall.setArgument(1, jsNumber(k));
    855865            cachedCall.setArgument(2, thisObj);
     
    910920        CachedCall cachedCall(exec, f, 3);
    911921        for (; k < length && !exec->hadException(); ++k) {
    912             if (UNLIKELY(!array->canGetIndex(k)))
     922            if (UNLIKELY(!array->canGetIndexQuickly(k)))
    913923                break;
    914924           
    915925            cachedCall.setThis(applyThis);
    916             cachedCall.setArgument(0, array->getIndex(k));
     926            cachedCall.setArgument(0, array->getIndexQuickly(k));
    917927            cachedCall.setArgument(1, jsNumber(k));
    918928            cachedCall.setArgument(2, thisObj);
     
    966976        CachedCall cachedCall(exec, f, 3);
    967977        for (; k < length && !exec->hadException(); ++k) {
    968             if (UNLIKELY(!array->canGetIndex(k)))
     978            if (UNLIKELY(!array->canGetIndexQuickly(k)))
    969979                break;
    970980
    971981            cachedCall.setThis(applyThis);
    972             cachedCall.setArgument(0, array->getIndex(k));
     982            cachedCall.setArgument(0, array->getIndexQuickly(k));
    973983            cachedCall.setArgument(1, jsNumber(k));
    974984            cachedCall.setArgument(2, thisObj);
     
    10181028        CachedCall cachedCall(exec, f, 3);
    10191029        for (; k < length && !exec->hadException(); ++k) {
    1020             if (UNLIKELY(!array->canGetIndex(k)))
     1030            if (UNLIKELY(!array->canGetIndexQuickly(k)))
    10211031                break;
    10221032           
    10231033            cachedCall.setThis(applyThis);
    1024             cachedCall.setArgument(0, array->getIndex(k));
     1034            cachedCall.setArgument(0, array->getIndexQuickly(k));
    10251035            cachedCall.setArgument(1, jsNumber(k));
    10261036            cachedCall.setArgument(2, thisObj);
     
    10761086    if (exec->argumentCount() >= 2)
    10771087        rv = exec->argument(1);
    1078     else if (array && array->canGetIndex(0)){
    1079         rv = array->getIndex(0);
     1088    else if (array && array->canGetIndexQuickly(0)) {
     1089        rv = array->getIndexQuickly(0);
    10801090        i = 1;
    10811091    } else {
     
    10981108            cachedCall.setArgument(0, rv);
    10991109            JSValue v;
    1100             if (LIKELY(array->canGetIndex(i)))
    1101                 v = array->getIndex(i);
     1110            if (LIKELY(array->canGetIndexQuickly(i)))
     1111                v = array->getIndexQuickly(i);
    11021112            else
    11031113                break; // length has been made unsafe while we enumerate fallback to slow path
     
    11531163    if (exec->argumentCount() >= 2)
    11541164        rv = exec->argument(1);
    1155     else if (array && array->canGetIndex(length - 1)){
    1156         rv = array->getIndex(length - 1);
     1165    else if (array && array->canGetIndexQuickly(length - 1)) {
     1166        rv = array->getIndexQuickly(length - 1);
    11571167        i = 1;
    11581168    } else {
     
    11751185            cachedCall.setThis(jsUndefined());
    11761186            cachedCall.setArgument(0, rv);
    1177             if (UNLIKELY(!array->canGetIndex(idx)))
     1187            if (UNLIKELY(!array->canGetIndexQuickly(idx)))
    11781188                break; // length has been made unsafe while we enumerate fallback to slow path
    1179             cachedCall.setArgument(1, array->getIndex(idx));
     1189            cachedCall.setArgument(1, array->getIndexQuickly(idx));
    11801190            cachedCall.setArgument(2, jsNumber(idx));
    11811191            cachedCall.setArgument(3, array);
  • trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h

    r116828 r128400  
    2929    class ArrayPrototype : public JSArray {
    3030    private:
    31         ArrayPrototype(JSGlobalObject*, Structure*);
     31        ArrayPrototype(JSGlobalObject*, Structure*, Butterfly*);
    3232
    3333    public:
    3434        typedef JSArray Base;
    3535
    36         static ArrayPrototype* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
    37         {
    38             ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(*exec->heap())) ArrayPrototype(globalObject, structure);
    39             prototype->finishCreation(globalObject);
    40             return prototype;
    41         }
     36        static ArrayPrototype* create(ExecState*, JSGlobalObject*, Structure*);
    4237       
    4338        static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
     
    4843        static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
    4944        {
    50             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
     45            return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
    5146        }
    5247
  • trunk/Source/JavaScriptCore/runtime/ClassInfo.h

    r127191 r128400  
    7373        GetOwnPropertyNamesFunctionPtr getOwnPropertyNames;
    7474
     75        typedef void (*GetOwnNonIndexPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     76        GetOwnNonIndexPropertyNamesFunctionPtr getOwnNonIndexPropertyNames;
     77
    7578        typedef void (*GetPropertyNamesFunctionPtr)(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    7679        GetPropertyNamesFunctionPtr getPropertyNames;
     
    125128        &ClassName::defaultValue, \
    126129        &ClassName::getOwnPropertyNames, \
     130        &ClassName::getOwnNonIndexPropertyNames, \
    127131        &ClassName::getPropertyNames, \
    128132        &ClassName::className, \
  • trunk/Source/JavaScriptCore/runtime/JSActivation.cpp

    r128260 r128400  
    108108}
    109109
    110 void JSActivation::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
     110void JSActivation::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    111111{
    112112    JSActivation* thisObject = jsCast<JSActivation*>(object);
     
    123123        propertyNames.add(Identifier(exec, it->first.get()));
    124124    }
    125     // Skip the JSVariableObject implementation of getOwnPropertyNames
    126     JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
     125    // Skip the JSVariableObject implementation of getOwnNonIndexPropertyNames
     126    JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
    127127}
    128128
  • trunk/Source/JavaScriptCore/runtime/JSActivation.h

    r128260 r128400  
    6666
    6767        static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
    68         static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     68        static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    6969        JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
    7070
  • 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
  • trunk/Source/JavaScriptCore/runtime/JSArray.h

    r127349 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 *
    55 *  This library is free software; you can redistribute it and/or
     
    2222#define JSArray_h
    2323
     24#include "ArrayConventions.h"
     25#include "ButterflyInlineMethods.h"
    2426#include "JSObject.h"
    25 
    26 #define CHECK_ARRAY_CONSISTENCY 0
    2727
    2828namespace JSC {
     
    3030    class JSArray;
    3131    class LLIntOffsetsExtractor;
    32 
    33     enum PutDirectIndexMode { PutDirectIndexLikePutDirect, PutDirectIndexShouldNotThrow, PutDirectIndexShouldThrow };
    34 
    35     struct SparseArrayEntry : public WriteBarrier<Unknown> {
    36         typedef WriteBarrier<Unknown> Base;
    37 
    38         SparseArrayEntry() : attributes(0) {}
    39 
    40         JSValue get(ExecState*, JSArray*) const;
    41         void get(PropertySlot&) const;
    42         void get(PropertyDescriptor&) const;
    43         JSValue getNonSparseMode() const;
    44 
    45         unsigned attributes;
    46     };
    47 
    48     class SparseArrayValueMap {
    49         typedef HashMap<uint64_t, SparseArrayEntry, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t> > Map;
    50 
    51         enum Flags {
    52             Normal = 0,
    53             SparseMode = 1,
    54             LengthIsReadOnly = 2,
    55         };
    56 
    57     public:
    58         typedef Map::iterator iterator;
    59         typedef Map::const_iterator const_iterator;
    60         typedef Map::AddResult AddResult;
    61 
    62         SparseArrayValueMap()
    63             : m_flags(Normal)
    64             , m_reportedCapacity(0)
    65         {
    66         }
    67 
    68         void visitChildren(SlotVisitor&);
    69 
    70         bool sparseMode()
    71         {
    72             return m_flags & SparseMode;
    73         }
    74 
    75         void setSparseMode()
    76         {
    77             m_flags = static_cast<Flags>(m_flags | SparseMode);
    78         }
    79 
    80         bool lengthIsReadOnly()
    81         {
    82             return m_flags & LengthIsReadOnly;
    83         }
    84 
    85         void setLengthIsReadOnly()
    86         {
    87             m_flags = static_cast<Flags>(m_flags | LengthIsReadOnly);
    88         }
    89 
    90         // These methods may mutate the contents of the map
    91         void put(ExecState*, JSArray*, unsigned, JSValue, bool shouldThrow);
    92         bool putDirect(ExecState*, JSArray*, unsigned, JSValue, PutDirectIndexMode);
    93         AddResult add(JSArray*, unsigned);
    94         iterator find(unsigned i) { return m_map.find(i); }
    95         // This should ASSERT the remove is valid (check the result of the find).
    96         void remove(iterator it) { m_map.remove(it); }
    97         void remove(unsigned i) { m_map.remove(i); }
    98 
    99         // These methods do not mutate the contents of the map.
    100         iterator notFound() { return m_map.end(); }
    101         bool isEmpty() const { return m_map.isEmpty(); }
    102         bool contains(unsigned i) const { return m_map.contains(i); }
    103         size_t size() const { return m_map.size(); }
    104         // Only allow const begin/end iteration.
    105         const_iterator begin() const { return m_map.begin(); }
    106         const_iterator end() const { return m_map.end(); }
    107 
    108     private:
    109         Map m_map;
    110         Flags m_flags;
    111         size_t m_reportedCapacity;
    112     };
    113 
    114     // This struct holds the actual data values of an array.  A JSArray object points to it's contained ArrayStorage
    115     // struct by pointing to m_vector.  To access the contained ArrayStorage struct, use the getStorage() and
    116     // setStorage() methods.  It is important to note that there may be space before the ArrayStorage that
    117     // is used to quick unshift / shift operation.  The actual allocated pointer is available by using:
    118     //     getStorage() - m_indexBias * sizeof(JSValue)
    119     struct ArrayStorage {
    120         unsigned m_length; // The "length" property on the array
    121         unsigned m_numValuesInVector;
    122         void* m_allocBase; // Pointer to base address returned by malloc().  Keeping this pointer does eliminate false positives from the leak detector.
    123 #if CHECK_ARRAY_CONSISTENCY
    124         // Needs to be a uintptr_t for alignment purposes.
    125         uintptr_t m_initializationIndex;
    126         uintptr_t m_inCompactInitialization;
    127 #else
    128         uintptr_t m_padding;
    129 #endif
    130         WriteBarrier<Unknown> m_vector[1];
    131 
    132         static ptrdiff_t lengthOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_length); }
    133         static ptrdiff_t numValuesInVectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector); }
    134         static ptrdiff_t allocBaseOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_allocBase); }
    135         static ptrdiff_t vectorOffset() { return OBJECT_OFFSETOF(ArrayStorage, m_vector); }
    136     };
    13732
    13833    class JSArray : public JSNonFinalObject {
     
    14136        friend class JIT;
    14237
     38    public:
     39        typedef JSNonFinalObject Base;
     40
    14341    protected:
    144         explicit JSArray(JSGlobalData& globalData, Structure* structure)
    145             : JSNonFinalObject(globalData, structure)
    146             , m_indexBias(0)
    147             , m_storage(0)
    148             , m_sparseValueMap(0)
     42        explicit JSArray(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly)
     43            : JSNonFinalObject(globalData, structure, butterfly)
    14944        {
    15045        }
    15146
    152         JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, unsigned initialLength = 0);
    153         JS_EXPORT_PRIVATE JSArray* tryFinishCreationUninitialized(JSGlobalData&, unsigned initialLength);
    154    
    15547    public:
    156         typedef JSNonFinalObject Base;
    157 
    158         static void finalize(JSCell*);
    159 
    16048        static JSArray* create(JSGlobalData&, Structure*, unsigned initialLength = 0);
    16149
     
    17058
    17159        static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
    172         JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
    17360        static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
    174         static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
    175         // This is similar to the JSObject::putDirect* methods:
    176         //  - the prototype chain is not consulted
    177         //  - accessors are not called.
    178         //  - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).
    179         // This method creates a property with attributes writable, enumerable and configurable all set to true.
    180         bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, PutDirectIndexMode mode = PutDirectIndexLikePutDirect)
    181         {
    182             if (canSetIndex(propertyName)) {
    183                 setIndex(exec->globalData(), propertyName, value);
    184                 return true;
    185             }
    186             return putDirectIndexBeyondVectorLength(exec, propertyName, value, mode);
    187         }
    18861
    18962        static JS_EXPORTDATA const ClassInfo s_info;
    19063       
    191         unsigned length() const { return m_storage->m_length; }
     64        unsigned length() const { return getArrayLength(); }
    19265        // OK to use on new arrays, but not if it might be a RegExpMatchArray.
    19366        bool setLength(ExecState*, unsigned, bool throwException = false);
     
    20376        bool unshiftCount(ExecState*, unsigned count);
    20477
    205         bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; }
    206         JSValue getIndex(unsigned i)
    207         {
    208             ASSERT(canGetIndex(i));
    209             return m_storage->m_vector[i].get();
    210         }
    211 
    212         bool canSetIndex(unsigned i) { return i < m_vectorLength; }
    213         void setIndex(JSGlobalData& globalData, unsigned i, JSValue v)
    214         {
    215             ASSERT(canSetIndex(i));
    216            
    217             WriteBarrier<Unknown>& x = m_storage->m_vector[i];
    218             if (!x) {
    219                 ArrayStorage *storage = m_storage;
    220                 ++storage->m_numValuesInVector;
    221                 if (i >= storage->m_length)
    222                     storage->m_length = i + 1;
    223             }
    224             x.set(globalData, this, v);
    225         }
    226        
    227         inline void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
    228         {
    229             ASSERT(canSetIndex(i));
    230             ArrayStorage *storage = m_storage;
    231 #if CHECK_ARRAY_CONSISTENCY
    232             ASSERT(storage->m_inCompactInitialization);
    233             // Check that we are initializing the next index in sequence.
    234             ASSERT(i == storage->m_initializationIndex);
    235             // tryCreateUninitialized set m_numValuesInVector to the initialLength,
    236             // check we do not try to initialize more than this number of properties.
    237             ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector);
    238             storage->m_initializationIndex++;
    239 #endif
    240             ASSERT(i < storage->m_length);
    241             ASSERT(i < storage->m_numValuesInVector);
    242             storage->m_vector[i].set(globalData, this, v);
    243         }
    244 
    245         inline void completeInitialization(unsigned newLength)
    246         {
    247             // Check that we have initialized as meny properties as we think we have.
    248             ASSERT_UNUSED(newLength, newLength == m_storage->m_length);
    249 #if CHECK_ARRAY_CONSISTENCY
    250             // Check that the number of propreties initialized matches the initialLength.
    251             ASSERT(m_storage->m_initializationIndex == m_storage->m_numValuesInVector);
    252             ASSERT(m_storage->m_inCompactInitialization);
    253             m_storage->m_inCompactInitialization = false;
    254 #endif
    255         }
    256 
    257         bool inSparseMode()
    258         {
    259             SparseArrayValueMap* map = m_sparseValueMap;
    260             return map && map->sparseMode();
    261         }
    262 
    26378        void fillArgList(ExecState*, MarkedArgumentBuffer&);
    26479        void copyToArguments(ExecState*, CallFrame*, uint32_t length);
     
    26681        static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
    26782        {
    268             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
     83            return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
    26984        }
    27085       
    271         static ptrdiff_t storageOffset()
    272         {
    273             return OBJECT_OFFSETOF(JSArray, m_storage);
    274         }
    275 
    276         static ptrdiff_t vectorLengthOffset()
    277         {
    278             return OBJECT_OFFSETOF(JSArray, m_vectorLength);
    279         }
    280 
    281         JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
    282 
    283         void enterDictionaryMode(JSGlobalData&);
    284 
    28586    protected:
    286         static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | OverridesGetPropertyNames | JSObject::StructureFlags;
     87        static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSObject::StructureFlags;
    28788        static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
    28889
    28990        static bool deleteProperty(JSCell*, ExecState*, PropertyName);
    290         static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
    291         static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     91        JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    29292
    29393    private:
    294         static size_t storageSize(unsigned vectorLength);
    29594        bool isLengthWritable()
    29695        {
    297             SparseArrayValueMap* map = m_sparseValueMap;
     96            ArrayStorage* storage = arrayStorageOrNull();
     97            if (!storage)
     98                return false;
     99            SparseArrayValueMap* map = storage->m_sparseMap.get();
    298100            return !map || !map->lengthIsReadOnly();
    299101        }
    300102
    301103        void setLengthWritable(ExecState*, bool writable);
    302         void putDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
    303         bool defineOwnNumericProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
    304         void allocateSparseMap(JSGlobalData&);
    305         void deallocateSparseMap();
    306 
    307         void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
    308         JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, PutDirectIndexMode);
    309 
    310         unsigned getNewVectorLength(unsigned desiredLength);
    311         bool increaseVectorLength(JSGlobalData&, unsigned newLength);
     104
    312105        bool unshiftCountSlowCase(JSGlobalData&, unsigned count);
    313106       
    314107        unsigned compactForSorting(JSGlobalData&);
    315 
    316         enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };
    317         void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck);
    318 
    319         unsigned m_vectorLength; // The valid length of m_vector
    320         unsigned m_indexBias; // The number of JSValue sized blocks before ArrayStorage.
    321         ArrayStorage *m_storage;
    322 
    323         // FIXME: Maybe SparseArrayValueMap should be put into its own JSCell?
    324         SparseArrayValueMap* m_sparseValueMap;
    325 
    326         static ptrdiff_t sparseValueMapOffset() { return OBJECT_OFFSETOF(JSArray, m_sparseValueMap); }
    327         static ptrdiff_t indexBiasOffset() { return OBJECT_OFFSETOF(JSArray, m_indexBias); }
    328108    };
    329109
     110    inline Butterfly* createArrayButterfly(JSGlobalData& globalData, unsigned initialLength)
     111    {
     112        Butterfly* butterfly = Butterfly::create(
     113            globalData, 0, 0, true, baseIndexingHeaderForArray(initialLength), ArrayStorage::sizeFor(BASE_VECTOR_LEN));
     114        ArrayStorage* storage = butterfly->arrayStorage();
     115        storage->m_indexBias = 0;
     116        storage->m_sparseMap.clear();
     117        storage->m_numValuesInVector = 0;
     118#if CHECK_ARRAY_CONSISTENCY
     119        storage->m_initializationIndex = 0;
     120        storage->m_inCompactInitialization = 0;
     121#endif
     122        return butterfly;
     123    }
     124
     125    Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData&, unsigned initialLength);
     126
    330127    inline JSArray* JSArray::create(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
    331128    {
    332         JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure);
    333         array->finishCreation(globalData, initialLength);
     129        Butterfly* butterfly = createArrayButterfly(globalData, initialLength);
     130        JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly);
     131        array->finishCreation(globalData);
    334132        return array;
    335133    }
     
    337135    inline JSArray* JSArray::tryCreateUninitialized(JSGlobalData& globalData, Structure* structure, unsigned initialLength)
    338136    {
    339         JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure);
    340         return array->tryFinishCreationUninitialized(globalData, initialLength);
     137        unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength);
     138        if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
     139            return 0;
     140       
     141        void* temp;
     142        if (!globalData.heap.tryAllocateStorage(Butterfly::totalSize(0, 0, true, ArrayStorage::sizeFor(vectorLength)), &temp))
     143            return 0;
     144        Butterfly* butterfly = Butterfly::fromBase(temp, 0, 0);
     145        *butterfly->indexingHeader() = indexingHeaderForArray(initialLength, vectorLength);
     146        ArrayStorage* storage = butterfly->arrayStorage();
     147        storage->m_indexBias = 0;
     148        storage->m_sparseMap.clear();
     149        storage->m_numValuesInVector = initialLength;
     150#if CHECK_ARRAY_CONSISTENCY
     151        storage->m_initializationIndex = 0;
     152        storage->m_inCompactInitialization = true;
     153#endif
     154       
     155        JSArray* array = new (NotNull, allocateCell<JSArray>(globalData.heap)) JSArray(globalData, structure, butterfly);
     156        array->finishCreation(globalData);
     157        return array;
    341158    }
    342159
     
    356173    inline bool isJSArray(JSCell* cell) { return cell->classInfo() == &JSArray::s_info; }
    357174    inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); }
    358 
    359 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
    360 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
    361 // size calculation cannot overflow.  (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) +
    362 // (vectorLength * sizeof(WriteBarrier<Unknown>)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
    363 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>))
    364 
    365 // These values have to be macros to be used in max() and min() without introducing
    366 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
    367 #define MIN_SPARSE_ARRAY_INDEX 10000U
    368 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
    369     inline size_t JSArray::storageSize(unsigned vectorLength)
    370     {
    371         ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
    372    
    373         // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
    374         // - as asserted above - the following calculation cannot overflow.
    375         size_t size = (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>)) + (vectorLength * sizeof(WriteBarrier<Unknown>));
    376         // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
    377         // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
    378         ASSERT(((size - (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))) / sizeof(WriteBarrier<Unknown>) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(WriteBarrier<Unknown>))));
    379    
    380         return size;
    381     }
    382175
    383176    inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
  • trunk/Source/JavaScriptCore/runtime/JSBoundFunction.cpp

    r127191 r128400  
    4646    MarkedArgumentBuffer args;
    4747    for (unsigned i = 0; i < boundArgs->length(); ++i)
    48         args.append(boundArgs->getIndex(i));
     48        args.append(boundArgs->getIndexQuickly(i));
    4949    for (unsigned i = 0; i < exec->argumentCount(); ++i)
    5050        args.append(exec->argument(i));
     
    6666    MarkedArgumentBuffer args;
    6767    for (unsigned i = 0; i < boundArgs->length(); ++i)
    68         args.append(boundArgs->getIndex(i));
     68        args.append(boundArgs->getIndexQuickly(i));
    6969    for (unsigned i = 0; i < exec->argumentCount(); ++i)
    7070        args.append(exec->argument(i));
     
    113113    ASSERT(inherits(&s_info));
    114114
    115     putDirectAccessor(exec->globalData(), exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    116     putDirectAccessor(exec->globalData(), exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     115    putDirectAccessor(exec, exec->propertyNames().arguments, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     116    putDirectAccessor(exec, exec->propertyNames().caller, globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    117117}
    118118
  • trunk/Source/JavaScriptCore/runtime/JSCell.cpp

    r127191 r128400  
    179179}
    180180
     181void JSCell::getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
     182{
     183    ASSERT_NOT_REACHED();
     184}
     185
    181186String JSCell::className(const JSObject*)
    182187{
  • trunk/Source/JavaScriptCore/runtime/JSCell.h

    r128260 r128400  
    139139            return &m_structure;
    140140        }
    141 
     141       
    142142#if ENABLE(GC_VALIDATION)
    143143        Structure* unvalidatedStructure() { return m_structure.unvalidatedGet(); }
     
    157157        static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
    158158        static NO_RETURN_DUE_TO_ASSERT void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     159        static NO_RETURN_DUE_TO_ASSERT void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    159160        static NO_RETURN_DUE_TO_ASSERT void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    160161        static String className(const JSObject*);
  • trunk/Source/JavaScriptCore/runtime/JSFunction.cpp

    r128265 r128400  
    223223            bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
    224224            if (!result) {
    225                 thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     225                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    226226                result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
    227227                ASSERT(result);
     
    247247            bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
    248248            if (!result) {
    249                 thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     249                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    250250                result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
    251251                ASSERT(result);
     
    276276            bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
    277277            if (!result) {
    278                 thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     278                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    279279                result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
    280280                ASSERT(result);
     
    300300            bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
    301301            if (!result) {
    302                 thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     302                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    303303                result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
    304304                ASSERT(result);
     
    313313}
    314314
    315 void JSFunction::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
     315void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    316316{
    317317    JSFunction* thisObject = jsCast<JSFunction*>(object);
     
    326326        propertyNames.add(exec->propertyNames().name);
    327327    }
    328     Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
     328    Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
    329329}
    330330
     
    395395        if (thisObject->jsExecutable()->isStrictMode()) {
    396396            if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor))
    397                 thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     397                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    398398            return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
    399399        }
     
    402402        if (thisObject->jsExecutable()->isStrictMode()) {
    403403            if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor))
    404                 thisObject->putDirectAccessor(exec->globalData(), propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
     404                thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
    405405            return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
    406406        }
  • trunk/Source/JavaScriptCore/runtime/JSFunction.h

    r128265 r128400  
    148148        static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
    149149        static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
    150         static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties);
     150        static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode = ExcludeDontEnumProperties);
    151151        static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
    152152
  • trunk/Source/JavaScriptCore/runtime/JSGlobalData.cpp

    r127719 r128400  
    5656#include "RegExpCache.h"
    5757#include "RegExpObject.h"
     58#include "SparseArrayValueMapInlineMethods.h"
    5859#include "StrictEvalActivation.h"
    5960#include "StrongInlines.h"
     
    214215    sharedSymbolTableStructure.set(*this, SharedSymbolTable::createStructure(*this, 0, jsNull()));
    215216    structureChainStructure.set(*this, StructureChain::createStructure(*this, 0, jsNull()));
     217    sparseArrayValueMapStructure.set(*this, SparseArrayValueMap::createStructure(*this, 0, jsNull()));
    216218
    217219    wtfThreadData().setCurrentIdentifierTable(existingEntryIdentifierTable);
  • trunk/Source/JavaScriptCore/runtime/JSGlobalData.h

    r127719 r128400  
    245245        Strong<Structure> sharedSymbolTableStructure;
    246246        Strong<Structure> structureChainStructure;
     247        Strong<Structure> sparseArrayValueMapStructure;
    247248
    248249        IdentifierTable* identifierTable;
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp

    r127958 r128400  
    214214    protoAccessor->setGetter(exec->globalData(), JSFunction::create(exec, this, 0, String(), globalFuncProtoGetter));
    215215    protoAccessor->setSetter(exec->globalData(), JSFunction::create(exec, this, 0, String(), globalFuncProtoSetter));
    216     m_objectPrototype->putDirectAccessor(exec->globalData(), exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum);
     216    m_objectPrototype->putDirectAccessor(exec, exec->propertyNames().underscoreProto, protoAccessor, Accessor | DontEnum);
    217217    m_functionPrototype->structure()->setPrototypeWithoutTransition(exec->globalData(), m_objectPrototype.get());
    218218
  • trunk/Source/JavaScriptCore/runtime/JSONObject.cpp

    r127958 r128400  
    522522        // Get the value.
    523523        JSValue value;
    524         if (m_isJSArray && asArray(m_object.get())->canGetIndex(index))
    525             value = asArray(m_object.get())->getIndex(index);
     524        if (m_isJSArray && asArray(m_object.get())->canGetIndexQuickly(index))
     525            value = asArray(m_object.get())->getIndexQuickly(index);
    526526        else {
    527527            PropertySlot slot(m_object.get());
     
    688688                    break;
    689689                }
    690                 if (isJSArray(array) && array->canGetIndex(index))
    691                     inValue = array->getIndex(index);
     690                if (isJSArray(array) && array->canGetIndexQuickly(index))
     691                    inValue = array->getIndexQuickly(index);
    692692                else {
    693693                    PropertySlot slot;
  • trunk/Source/JavaScriptCore/runtime/JSObject.cpp

    r127505 r128400  
    22 *  Copyright (C) 1999-2001 Harri Porten ([email protected])
    33 *  Copyright (C) 2001 Peter Kelly ([email protected])
    4  *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
     4 *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012 Apple Inc. All rights reserved.
    55 *  Copyright (C) 2007 Eric Seidel ([email protected])
    66 *
     
    2525#include "JSObject.h"
    2626
     27#include "ButterflyInlineMethods.h"
    2728#include "CopiedSpaceInlineMethods.h"
    2829#include "DatePrototype.h"
    2930#include "ErrorConstructor.h"
    3031#include "GetterSetter.h"
     32#include "IndexingHeaderInlineMethods.h"
    3133#include "JSFunction.h"
    3234#include "JSGlobalObject.h"
     
    3941#include "PropertyDescriptor.h"
    4042#include "PropertyNameArray.h"
     43#include "Reject.h"
    4144#include "SlotVisitorInlineMethods.h"
     45#include "SparseArrayValueMapInlineMethods.h"
    4246#include <math.h>
    4347#include <wtf/Assertions.h>
    4448
    4549namespace JSC {
     50
     51// We keep track of the size of the last array after it was grown. We use this
     52// as a simple heuristic for as the value to grow the next array from size 0.
     53// This value is capped by the constant FIRST_VECTOR_GROW defined above.
     54static unsigned lastArraySize = 0;
    4655
    4756JSCell* getCallableObjectSlow(JSCell* cell)
     
    8796}
    8897
    89 ALWAYS_INLINE void JSObject::visitOutOfLineStorage(SlotVisitor& visitor, PropertyStorage storage, size_t storageSize)
    90 {
    91     ASSERT(storage);
    92     ASSERT(storageSize);
    93    
    94     size_t capacity = structure()->outOfLineCapacity();
    95     ASSERT(capacity);
    96     size_t capacityInBytes = capacity * sizeof(WriteBarrierBase<Unknown>);
    97     PropertyStorage baseOfStorage = storage - capacity - 1;
    98     if (visitor.checkIfShouldCopyAndPinOtherwise(baseOfStorage, capacityInBytes)) {
    99         PropertyStorage newBaseOfStorage = static_cast<PropertyStorage>(visitor.allocateNewSpace(capacityInBytes));
    100         PropertyStorage currentTarget = newBaseOfStorage + capacity;
    101         PropertyStorage newStorage = currentTarget + 1;
    102         PropertyStorage currentSource = storage - 1;
     98ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize)
     99{
     100    ASSERT(butterfly);
     101   
     102    Structure* structure = this->structure();
     103   
     104    size_t propertyCapacity = structure->outOfLineCapacity();
     105    size_t preCapacity;
     106    size_t indexingPayloadSizeInBytes;
     107    bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType());
     108    if (UNLIKELY(hasIndexingHeader)) {
     109        preCapacity = butterfly->indexingHeader()->preCapacity(structure);
     110        indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
     111    } else {
     112        preCapacity = 0;
     113        indexingPayloadSizeInBytes = 0;
     114    }
     115    size_t capacityInBytes = Butterfly::totalSize(
     116        preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
     117    if (visitor.checkIfShouldCopyAndPinOtherwise(
     118            butterfly->base(preCapacity, propertyCapacity), capacityInBytes)) {
     119        Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
     120       
     121        // Mark and copy the properties.
     122        PropertyStorage currentTarget = newButterfly->propertyStorage();
     123        PropertyStorage currentSource = butterfly->propertyStorage();
    103124        for (size_t count = storageSize; count--;) {
    104125            JSValue value = (--currentSource)->get();
     
    107128            (--currentTarget)->setWithoutWriteBarrier(value);
    108129        }
    109         m_outOfLineStorage.set(newStorage, StorageBarrier::Unchecked);
    110     } else
    111         visitor.appendValues(storage - storageSize - 1, storageSize);
     130       
     131        if (UNLIKELY(hasIndexingHeader)) {
     132            *newButterfly->indexingHeader() = *butterfly->indexingHeader();
     133           
     134            // Mark and copy the array if appropriate.
     135            switch (structure->indexingType()) {
     136            case ArrayWithArrayStorage:
     137            case NonArrayWithArrayStorage: {
     138                newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage());
     139                WriteBarrier<Unknown>* currentTarget = newButterfly->arrayStorage()->m_vector;
     140                WriteBarrier<Unknown>* currentSource = butterfly->arrayStorage()->m_vector;
     141                for (size_t count = newButterfly->arrayStorage()->vectorLength(); count--;) {
     142                    JSValue value = (currentSource++)->get();
     143                    if (value)
     144                        visitor.appendUnbarrieredValue(&value);
     145                    (currentTarget++)->setWithoutWriteBarrier(value);
     146                }
     147                if (newButterfly->arrayStorage()->m_sparseMap)
     148                    visitor.append(&newButterfly->arrayStorage()->m_sparseMap);
     149                break;
     150            }
     151            default:
     152                break;
     153            }
     154        }
     155       
     156        m_butterfly = newButterfly;
     157    } else {
     158        // Mark the properties.
     159        visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize);
     160       
     161        // Mark the array if appropriate.
     162        switch (structure->indexingType()) {
     163        case ArrayWithArrayStorage:
     164        case NonArrayWithArrayStorage:
     165            visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
     166            if (butterfly->arrayStorage()->m_sparseMap)
     167                visitor.append(&butterfly->arrayStorage()->m_sparseMap);
     168            break;
     169        default:
     170            break;
     171        }
     172    }
    112173}
    113174
     
    123184    JSCell::visitChildren(thisObject, visitor);
    124185
    125     PropertyStorage storage = thisObject->outOfLineStorage();
    126     if (storage)
    127         thisObject->visitOutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownNonFinalObject());
     186    Butterfly* butterfly = thisObject->butterfly();
     187    if (butterfly)
     188        thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownNonFinalObject());
    128189
    129190#if !ASSERT_DISABLED
     
    143204    JSCell::visitChildren(thisObject, visitor);
    144205
    145     PropertyStorage storage = thisObject->outOfLineStorage();
    146     if (storage)
    147         thisObject->visitOutOfLineStorage(visitor, storage, thisObject->structure()->outOfLineSizeForKnownFinalObject());
     206    Butterfly* butterfly = thisObject->butterfly();
     207    if (butterfly)
     208        thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSizeForKnownFinalObject());
    148209
    149210    size_t storageSize = thisObject->structure()->inlineSizeForKnownFinalObject();
     
    162223}
    163224
    164 bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
    165 {
     225bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot)
     226{
     227    // NB. The fact that we're directly consulting our indexed storage implies that it is not
     228    // legal for anyone to override getOwnPropertySlot() without also overriding
     229    // getOwnPropertySlotByIndex().
     230   
    166231    JSObject* thisObject = jsCast<JSObject*>(cell);
    167     return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
     232   
     233    if (i > MAX_ARRAY_INDEX)
     234        return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
     235   
     236    switch (thisObject->structure()->indexingType()) {
     237    case NonArray:
     238    case Array:
     239        break;
     240       
     241    case NonArrayWithArrayStorage:
     242    case ArrayWithArrayStorage: {
     243        ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
     244        if (i >= storage->length())
     245            return false;
     246       
     247        if (i < storage->vectorLength()) {
     248            JSValue value = storage->m_vector[i].get();
     249            if (value) {
     250                slot.setValue(value);
     251                return true;
     252            }
     253        } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
     254            SparseArrayValueMap::iterator it = map->find(i);
     255            if (it != map->notFound()) {
     256                it->second.get(slot);
     257                return true;
     258            }
     259        }
     260        break;
     261    }
     262       
     263    default:
     264        ASSERT_NOT_REACHED();
     265        break;
     266    }
     267   
     268    return false;
    168269}
    169270
     
    175276    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
    176277    JSGlobalData& globalData = exec->globalData();
     278   
     279    // Try indexed put first. This is required for correctness, since loads on property names that appear like
     280    // valid indices will never look in the named property storage.
     281    unsigned i = propertyName.asIndex();
     282    if (i != PropertyName::NotAnIndex) {
     283        putByIndex(thisObject, exec, i, value, slot.isStrictMode());
     284        return;
     285    }
    177286
    178287    // Check if there are any setters or getters in the prototype chain
     
    236345void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
    237346{
    238     PutPropertySlot slot(shouldThrow);
    239347    JSObject* thisObject = jsCast<JSObject*>(cell);
    240     thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
     348    thisObject->checkIndexingConsistency();
     349   
     350    if (UNLIKELY(propertyName > MAX_ARRAY_INDEX)) {
     351        PutPropertySlot slot(shouldThrow);
     352        thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
     353        return;
     354    }
     355   
     356    switch (thisObject->structure()->indexingType()) {
     357    case NonArray:
     358    case Array:
     359        break;
     360       
     361    case NonArrayWithArrayStorage:
     362    case ArrayWithArrayStorage: {
     363        ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
     364       
     365        if (propertyName >= storage->vectorLength())
     366            break;
     367       
     368        WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
     369        unsigned length = storage->length();
     370       
     371        // Update length & m_numValuesInVector as necessary.
     372        if (propertyName >= length) {
     373            length = propertyName + 1;
     374            storage->setLength(length);
     375            ++storage->m_numValuesInVector;
     376        } else if (!valueSlot)
     377            ++storage->m_numValuesInVector;
     378       
     379        valueSlot.set(exec->globalData(), thisObject, value);
     380        thisObject->checkIndexingConsistency();
     381        return;
     382    }
     383       
     384    default:
     385        ASSERT_NOT_REACHED();
     386    }
     387   
     388    thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
     389    thisObject->checkIndexingConsistency();
     390}
     391
     392ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData& globalData, ArrayStorage* storage)
     393{
     394    SparseArrayValueMap* map = storage->m_sparseMap.get();
     395
     396    if (!map)
     397        map = allocateSparseIndexMap(globalData);
     398
     399    if (map->sparseMode())
     400        return storage;
     401
     402    map->setSparseMode();
     403
     404    unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
     405    for (unsigned i = 0; i < usedVectorLength; ++i) {
     406        JSValue value = storage->m_vector[i].get();
     407        // This will always be a new entry in the map, so no need to check we can write,
     408        // and attributes are default so no need to set them.
     409        if (value)
     410            map->add(this, i).iterator->second.set(globalData, this, value);
     411    }
     412
     413    Butterfly* newButterfly = storage->butterfly()->resizeArray(globalData, structure(), 0, ArrayStorage::sizeFor(0));
     414    if (!newButterfly)
     415        CRASH();
     416   
     417    m_butterfly = newButterfly;
     418    newButterfly->arrayStorage()->m_indexBias = 0;
     419    newButterfly->arrayStorage()->setVectorLength(0);
     420    newButterfly->arrayStorage()->m_sparseMap.set(globalData, this, map);
     421   
     422    return newButterfly->arrayStorage();
     423}
     424
     425void JSObject::enterDictionaryIndexingMode(JSGlobalData& globalData)
     426{
     427    switch (structure()->indexingType()) {
     428    case ArrayWithArrayStorage:
     429    case NonArrayWithArrayStorage:
     430        enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage());
     431        break;
     432       
     433    default:
     434        break;
     435    }
     436}
     437
     438ArrayStorage* JSObject::createArrayStorage(JSGlobalData& globalData, unsigned length, unsigned vectorLength)
     439{
     440    IndexingType oldType = structure()->indexingType();
     441    ASSERT_UNUSED(oldType, oldType == NonArray || oldType == Array);
     442    Butterfly* newButterfly = m_butterfly->growArrayRight(
     443        globalData, structure(), structure()->outOfLineCapacity(), false, 0,
     444        ArrayStorage::sizeFor(vectorLength));
     445    if (!newButterfly)
     446        CRASH();
     447    ArrayStorage* result = newButterfly->arrayStorage();
     448    result->setLength(length);
     449    result->setVectorLength(vectorLength);
     450    result->m_sparseMap.clear();
     451    result->m_numValuesInVector = 0;
     452    result->m_indexBias = 0;
     453#if CHECK_ARRAY_CONSISTENCY
     454    result->m_initializationIndex = 0;
     455    result->m_inCompactInitialization = 0;
     456#endif
     457    Structure* newStructure = Structure::nonPropertyTransition(globalData, structure(), AllocateArrayStorage);
     458    setButterfly(globalData, newButterfly, newStructure);
     459    return result;
     460}
     461
     462ArrayStorage* JSObject::createInitialArrayStorage(JSGlobalData& globalData)
     463{
     464    return createArrayStorage(globalData, 0, BASE_VECTOR_LEN);
     465}
     466
     467ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData& globalData)
     468{
     469    switch (structure()->indexingType()) {
     470    case ArrayWithArrayStorage:
     471    case NonArrayWithArrayStorage:
     472        return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(globalData, m_butterfly->arrayStorage());
     473       
     474    case Array:
     475    case NonArray: {
     476        createArrayStorage(globalData, 0, 0);
     477        SparseArrayValueMap* map = allocateSparseIndexMap(globalData);
     478        map->setSparseMode();
     479        return arrayStorage();
     480    }
     481       
     482    default:
     483        ASSERT_NOT_REACHED();
     484        return 0;
     485    }
    241486}
    242487
     
    270515}
    271516
    272 void JSObject::putDirectAccessor(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
     517void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
    273518{
    274519    ASSERT(value.isGetterSetter() && (attributes & Accessor));
     520
     521    unsigned index = propertyName.asIndex();
     522    if (index != PropertyName::NotAnIndex) {
     523        putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect);
     524        return;
     525    }
     526
     527    JSGlobalData& globalData = exec->globalData();
    275528
    276529    PutPropertySlot slot;
     
    305558{
    306559    JSObject* thisObject = jsCast<JSObject*>(cell);
     560   
     561    unsigned i = propertyName.asIndex();
     562    if (i != PropertyName::NotAnIndex)
     563        return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i);
    307564
    308565    if (!thisObject->staticFunctionsReified())
     
    333590}
    334591
    335 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
     592bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
    336593{
    337594    JSObject* thisObject = jsCast<JSObject*>(cell);
    338     return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName));
     595   
     596    if (i > MAX_ARRAY_INDEX)
     597        return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i));
     598   
     599    switch (thisObject->structure()->indexingType()) {
     600    case Array:
     601    case NonArray:
     602        return true;
     603       
     604    case ArrayWithArrayStorage:
     605    case NonArrayWithArrayStorage: {
     606        ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
     607       
     608        if (i < storage->vectorLength()) {
     609            WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
     610            if (valueSlot) {
     611                valueSlot.clear();
     612                --storage->m_numValuesInVector;
     613            }
     614        } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
     615            SparseArrayValueMap::iterator it = map->find(i);
     616            if (it != map->notFound()) {
     617                if (it->second.attributes & DontDelete)
     618                    return false;
     619                map->remove(it);
     620            }
     621        }
     622       
     623        thisObject->checkIndexingConsistency();
     624        return true;
     625    }
     626       
     627    default:
     628        ASSERT_NOT_REACHED();
     629        return false;
     630    }
    339631}
    340632
     
    466758void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    467759{
     760    // Add numeric properties first. That appears to be the accepted convention.
     761    // FIXME: Filling PropertyNameArray with an identifier for every integer
     762    // is incredibly inefficient for large arrays. We need a different approach,
     763    // which almost certainly means a different structure for PropertyNameArray.
     764    switch (object->structure()->indexingType()) {
     765    case NonArray:
     766    case Array:
     767        break;
     768       
     769    case NonArrayWithArrayStorage:
     770    case ArrayWithArrayStorage: {
     771        ArrayStorage* storage = object->m_butterfly->arrayStorage();
     772       
     773        unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
     774        for (unsigned i = 0; i < usedVectorLength; ++i) {
     775            if (storage->m_vector[i])
     776                propertyNames.add(Identifier::from(exec, i));
     777        }
     778       
     779        if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
     780            Vector<unsigned> keys;
     781            keys.reserveCapacity(map->size());
     782           
     783            SparseArrayValueMap::const_iterator end = map->end();
     784            for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
     785                if (mode == IncludeDontEnumProperties || !(it->second.attributes & DontEnum))
     786                    keys.append(static_cast<unsigned>(it->first));
     787            }
     788           
     789            std::sort(keys.begin(), keys.end());
     790            for (unsigned i = 0; i < keys.size(); ++i)
     791                propertyNames.add(Identifier::from(exec, keys[i]));
     792        }
     793        break;
     794    }
     795       
     796    default:
     797        ASSERT_NOT_REACHED();
     798    }
     799   
     800    object->methodTable()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
     801}
     802
     803void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
     804{
    468805    getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
    469806    object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode);
     
    516853void JSObject::preventExtensions(JSGlobalData& globalData)
    517854{
    518     if (isJSArray(this))
    519         asArray(this)->enterDictionaryMode(globalData);
     855    enterDictionaryIndexingMode(globalData);
    520856    if (isExtensible())
    521857        setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure()));
     
    574910}
    575911
    576 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
    577 {
    578     if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
     912NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyOffset offset)
     913{
     914    if (JSObject* getterFunction = asGetterSetter(getDirectOffset(offset))->getter()) {
    579915        if (!structure()->isDictionary())
    580             slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
     916            slot.setCacheableGetterSlot(this, getterFunction, offset);
    581917        else
    582918            slot.setGetterSlot(getterFunction);
     
    604940}
    605941
    606 PropertyStorage JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
     942void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor)
     943{
     944    if (descriptor.isDataDescriptor()) {
     945        if (descriptor.value())
     946            entryInMap->set(exec->globalData(), this, descriptor.value());
     947        else if (oldDescriptor.isAccessorDescriptor())
     948            entryInMap->set(exec->globalData(), this, jsUndefined());
     949        entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor;
     950        return;
     951    }
     952
     953    if (descriptor.isAccessorDescriptor()) {
     954        JSObject* getter = 0;
     955        if (descriptor.getterPresent())
     956            getter = descriptor.getterObject();
     957        else if (oldDescriptor.isAccessorDescriptor())
     958            getter = oldDescriptor.getterObject();
     959        JSObject* setter = 0;
     960        if (descriptor.setterPresent())
     961            setter = descriptor.setterObject();
     962        else if (oldDescriptor.isAccessorDescriptor())
     963            setter = oldDescriptor.setterObject();
     964
     965        GetterSetter* accessor = GetterSetter::create(exec);
     966        if (getter)
     967            accessor->setGetter(exec->globalData(), getter);
     968        if (setter)
     969            accessor->setSetter(exec->globalData(), setter);
     970
     971        entryInMap->set(exec->globalData(), this, accessor);
     972        entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
     973        return;
     974    }
     975
     976    ASSERT(descriptor.isGenericDescriptor());
     977    entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor);
     978}
     979
     980// Defined in ES5.1 8.12.9
     981bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException)
     982{
     983    ASSERT(index != 0xFFFFFFFF);
     984
     985    if (!inSparseIndexingMode()) {
     986        // Fast case: we're putting a regular property to a regular array
     987        // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
     988        // however if the property currently exists missing attributes will override from their current 'true'
     989        // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode').
     990        if (!descriptor.attributes()) {
     991            ASSERT(!descriptor.isAccessorDescriptor());
     992            return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
     993        }
     994
     995        ensureArrayStorageExistsAndEnterDictionaryIndexingMode(exec->globalData());
     996    }
     997
     998    SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get();
     999    ASSERT(map);
     1000
     1001    // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
     1002    SparseArrayValueMap::AddResult result = map->add(this, index);
     1003    SparseArrayEntry* entryInMap = &result.iterator->second;
     1004
     1005    // 2. Let extensible be the value of the [[Extensible]] internal property of O.
     1006    // 3. If current is undefined and extensible is false, then Reject.
     1007    // 4. If current is undefined and extensible is true, then
     1008    if (result.isNewEntry) {
     1009        if (!isExtensible()) {
     1010            map->remove(result.iterator);
     1011            return reject(exec, throwException, "Attempting to define property on object that is not extensible.");
     1012        }
     1013
     1014        // 4.a. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then create an own data property
     1015        // named P of object O whose [[Value]], [[Writable]], [[Enumerable]] and [[Configurable]] attribute values
     1016        // are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly
     1017        // created property is set to its default value.
     1018        // 4.b. Else, Desc must be an accessor Property Descriptor so, create an own accessor property named P of
     1019        // object O whose [[Get]], [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by
     1020        // Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property
     1021        // is set to its default value.
     1022        // 4.c. Return true.
     1023
     1024        PropertyDescriptor defaults;
     1025        entryInMap->setWithoutWriteBarrier(jsUndefined());
     1026        entryInMap->attributes = DontDelete | DontEnum | ReadOnly;
     1027        entryInMap->get(defaults);
     1028
     1029        putIndexedDescriptor(exec, entryInMap, descriptor, defaults);
     1030        if (index >= m_butterfly->arrayStorage()->length())
     1031            m_butterfly->arrayStorage()->setLength(index + 1);
     1032        return true;
     1033    }
     1034
     1035    // 5. Return true, if every field in Desc is absent.
     1036    // 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).
     1037    PropertyDescriptor current;
     1038    entryInMap->get(current);
     1039    if (descriptor.isEmpty() || descriptor.equalTo(exec, current))
     1040        return true;
     1041
     1042    // 7. If the [[Configurable]] field of current is false then
     1043    if (!current.configurable()) {
     1044        // 7.a. Reject, if the [[Configurable]] field of Desc is true.
     1045        if (descriptor.configurablePresent() && descriptor.configurable())
     1046            return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
     1047        // 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.
     1048        if (descriptor.enumerablePresent() && current.enumerable() != descriptor.enumerable())
     1049            return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
     1050    }
     1051
     1052    // 8. If IsGenericDescriptor(Desc) is true, then no further validation is required.
     1053    if (!descriptor.isGenericDescriptor()) {
     1054        // 9. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then
     1055        if (current.isDataDescriptor() != descriptor.isDataDescriptor()) {
     1056            // 9.a. Reject, if the [[Configurable]] field of current is false.
     1057            if (!current.configurable())
     1058                return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
     1059            // 9.b. If IsDataDescriptor(current) is true, then convert the property named P of object O from a
     1060            // data property to an accessor property. Preserve the existing values of the converted property's
     1061            // [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to
     1062            // their default values.
     1063            // 9.c. Else, convert the property named P of object O from an accessor property to a data property.
     1064            // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]]
     1065            // attributes and set the rest of the property's attributes to their default values.
     1066        } else if (current.isDataDescriptor() && descriptor.isDataDescriptor()) {
     1067            // 10. Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
     1068            // 10.a. If the [[Configurable]] field of current is false, then
     1069            if (!current.configurable() && !current.writable()) {
     1070                // 10.a.i. Reject, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true.
     1071                if (descriptor.writable())
     1072                    return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
     1073                // 10.a.ii. If the [[Writable]] field of current is false, then
     1074                // 10.a.ii.1. Reject, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false.
     1075                if (descriptor.value() && !sameValue(exec, descriptor.value(), current.value()))
     1076                    return reject(exec, throwException, "Attempting to change value of a readonly property.");
     1077            }
     1078            // 10.b. else, the [[Configurable]] field of current is true, so any change is acceptable.
     1079        } else {
     1080            ASSERT(current.isAccessorDescriptor() && current.getterPresent() && current.setterPresent());
     1081            // 11. Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true so, if the [[Configurable]] field of current is false, then
     1082            if (!current.configurable()) {
     1083                // 11.i. Reject, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false.
     1084                if (descriptor.setterPresent() && descriptor.setter() != current.setter())
     1085                    return reject(exec, throwException, "Attempting to change the setter of an unconfigurable property.");
     1086                // 11.ii. Reject, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false.
     1087                if (descriptor.getterPresent() && descriptor.getter() != current.getter())
     1088                    return reject(exec, throwException, "Attempting to change the getter of an unconfigurable property.");
     1089            }
     1090        }
     1091    }
     1092
     1093    // 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.
     1094    putIndexedDescriptor(exec, entryInMap, descriptor, current);
     1095    // 13. Return true.
     1096    return true;
     1097}
     1098
     1099SparseArrayValueMap* JSObject::allocateSparseIndexMap(JSGlobalData& globalData)
     1100{
     1101    SparseArrayValueMap* result = SparseArrayValueMap::create(globalData);
     1102    arrayStorage()->m_sparseMap.set(globalData, this, result);
     1103    return result;
     1104}
     1105
     1106void JSObject::deallocateSparseIndexMap()
     1107{
     1108    if (ArrayStorage* arrayStorage = arrayStorageOrNull())
     1109        arrayStorage->m_sparseMap.clear();
     1110}
     1111
     1112void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
     1113{
     1114    JSGlobalData& globalData = exec->globalData();
     1115
     1116    // i should be a valid array index that is outside of the current vector.
     1117    ASSERT(i <= MAX_ARRAY_INDEX);
     1118    ASSERT(i >= storage->vectorLength());
     1119   
     1120    SparseArrayValueMap* map = storage->m_sparseMap.get();
     1121   
     1122    // First, handle cases where we don't currently have a sparse map.
     1123    if (LIKELY(!map)) {
     1124        // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
     1125        ASSERT(isExtensible());
     1126   
     1127        // Update m_length if necessary.
     1128        if (i >= storage->length())
     1129            storage->setLength(i + 1);
     1130
     1131        // Check that it is sensible to still be using a vector, and then try to grow the vector.
     1132        if (LIKELY((isDenseEnoughForVector(i, storage->m_numValuesInVector)) && increaseVectorLength(globalData, i + 1))) {
     1133            // success! - reread m_storage since it has likely been reallocated, and store to the vector.
     1134            storage = arrayStorage();
     1135            storage->m_vector[i].set(globalData, this, value);
     1136            ++storage->m_numValuesInVector;
     1137            return;
     1138        }
     1139        // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
     1140        map = allocateSparseIndexMap(exec->globalData());
     1141        map->putEntry(exec, this, i, value, shouldThrow);
     1142        return;
     1143    }
     1144
     1145    // Update m_length if necessary.
     1146    unsigned length = storage->length();
     1147    if (i >= length) {
     1148        // Prohibit growing the array if length is not writable.
     1149        if (map->lengthIsReadOnly() || !isExtensible()) {
     1150            if (shouldThrow)
     1151                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
     1152            return;
     1153        }
     1154        length = i + 1;
     1155        storage->setLength(length);
     1156    }
     1157
     1158    // We are currently using a map - check whether we still want to be doing so.
     1159    // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
     1160    unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
     1161    if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length)) {
     1162        map->putEntry(exec, this, i, value, shouldThrow);
     1163        return;
     1164    }
     1165
     1166    // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
     1167    storage = arrayStorage();
     1168    storage->m_numValuesInVector = numValuesInArray;
     1169
     1170    // Copy all values from the map into the vector, and delete the map.
     1171    WriteBarrier<Unknown>* vector = storage->m_vector;
     1172    SparseArrayValueMap::const_iterator end = map->end();
     1173    for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
     1174        vector[it->first].set(globalData, this, it->second.getNonSparseMode());
     1175    deallocateSparseIndexMap();
     1176
     1177    // Store the new property into the vector.
     1178    WriteBarrier<Unknown>& valueSlot = vector[i];
     1179    if (!valueSlot)
     1180        ++storage->m_numValuesInVector;
     1181    valueSlot.set(globalData, this, value);
     1182}
     1183
     1184void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
     1185{
     1186    JSGlobalData& globalData = exec->globalData();
     1187
     1188    // i should be a valid array index that is outside of the current vector.
     1189    ASSERT(i <= MAX_ARRAY_INDEX);
     1190   
     1191    switch (structure()->indexingType()) {
     1192    case NonArray:
     1193    case Array: {
     1194        if (indexingShouldBeSparse()) {
     1195            putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData));
     1196            break;
     1197        }
     1198        if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH) {
     1199            putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, createArrayStorage(globalData, 0, 0));
     1200            break;
     1201        }
     1202       
     1203        ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1));
     1204        storage->m_vector[i].set(globalData, this, value);
     1205        storage->m_numValuesInVector = 1;
     1206        break;
     1207    }
     1208
     1209    case NonArrayWithArrayStorage:
     1210    case ArrayWithArrayStorage:
     1211        putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
     1212        break;
     1213       
     1214    default:
     1215        ASSERT_NOT_REACHED();
     1216    }
     1217}
     1218
     1219bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
     1220{
     1221    JSGlobalData& globalData = exec->globalData();
     1222
     1223    // i should be a valid array index that is outside of the current vector.
     1224    ASSERT(i >= storage->vectorLength() || attributes);
     1225    ASSERT(i <= MAX_ARRAY_INDEX);
     1226
     1227    SparseArrayValueMap* map = storage->m_sparseMap.get();
     1228
     1229    // First, handle cases where we don't currently have a sparse map.
     1230    if (LIKELY(!map)) {
     1231        // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
     1232        ASSERT(isExtensible());
     1233   
     1234        // Update m_length if necessary.
     1235        if (i >= storage->length())
     1236            storage->setLength(i + 1);
     1237
     1238        // Check that it is sensible to still be using a vector, and then try to grow the vector.
     1239        if (LIKELY(
     1240                !attributes
     1241                && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
     1242                && increaseVectorLength(globalData, i + 1))) {
     1243            // success! - reread m_storage since it has likely been reallocated, and store to the vector.
     1244            storage = arrayStorage();
     1245            storage->m_vector[i].set(globalData, this, value);
     1246            ++storage->m_numValuesInVector;
     1247            return true;
     1248        }
     1249        // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
     1250        map = allocateSparseIndexMap(exec->globalData());
     1251        return map->putDirect(exec, this, i, value, attributes, mode);
     1252    }
     1253
     1254    // Update m_length if necessary.
     1255    unsigned length = storage->length();
     1256    if (i >= length) {
     1257        if (mode != PutDirectIndexLikePutDirect) {
     1258            // Prohibit growing the array if length is not writable.
     1259            if (map->lengthIsReadOnly())
     1260                return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);
     1261            if (!isExtensible())
     1262                return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
     1263        }
     1264        length = i + 1;
     1265        storage->setLength(length);
     1266    }
     1267
     1268    // We are currently using a map - check whether we still want to be doing so.
     1269    // We will continue  to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
     1270    unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
     1271    if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->globalData(), length))
     1272        return map->putDirect(exec, this, i, value, attributes, mode);
     1273
     1274    // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
     1275    storage = arrayStorage();
     1276    storage->m_numValuesInVector = numValuesInArray;
     1277
     1278    // Copy all values from the map into the vector, and delete the map.
     1279    WriteBarrier<Unknown>* vector = storage->m_vector;
     1280    SparseArrayValueMap::const_iterator end = map->end();
     1281    for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
     1282        vector[it->first].set(globalData, this, it->second.getNonSparseMode());
     1283    deallocateSparseIndexMap();
     1284
     1285    // Store the new property into the vector.
     1286    WriteBarrier<Unknown>& valueSlot = vector[i];
     1287    if (!valueSlot)
     1288        ++storage->m_numValuesInVector;
     1289    valueSlot.set(globalData, this, value);
     1290    return true;
     1291}
     1292
     1293bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
     1294{
     1295    JSGlobalData& globalData = exec->globalData();
     1296
     1297    // i should be a valid array index that is outside of the current vector.
     1298    ASSERT(i <= MAX_ARRAY_INDEX);
     1299   
     1300    switch (structure()->indexingType()) {
     1301    case NonArray:
     1302    case Array: {
     1303        if (indexingShouldBeSparse() || attributes)
     1304            return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(globalData));
     1305        if (!isDenseEnoughForVector(i, 0) || i >= MAX_STORAGE_VECTOR_LENGTH)
     1306            return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, createArrayStorage(globalData, 0, 0));
     1307       
     1308        ArrayStorage* storage = createArrayStorage(globalData, i + 1, getNewVectorLength(0, 0, i + 1));
     1309        storage->m_vector[i].set(globalData, this, value);
     1310        storage->m_numValuesInVector = 1;
     1311        return true;
     1312    }
     1313
     1314    case NonArrayWithArrayStorage:
     1315    case ArrayWithArrayStorage:
     1316        return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
     1317       
     1318    default:
     1319        ASSERT_NOT_REACHED();
     1320        return false;
     1321    }
     1322}
     1323
     1324ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
     1325{
     1326    ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
     1327
     1328    unsigned increasedLength;
     1329    unsigned maxInitLength = std::min(currentLength, 100000U);
     1330
     1331    if (desiredLength < maxInitLength)
     1332        increasedLength = maxInitLength;
     1333    else if (!currentVectorLength)
     1334        increasedLength = std::max(desiredLength, lastArraySize);
     1335    else {
     1336        // Mathematically equivalent to:
     1337        //   increasedLength = (newLength * 3 + 1) / 2;
     1338        // or:
     1339        //   increasedLength = (unsigned)ceil(newLength * 1.5));
     1340        // This form is not prone to internal overflow.
     1341        increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1);
     1342    }
     1343
     1344    ASSERT(increasedLength >= desiredLength);
     1345
     1346    lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW);
     1347
     1348    return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
     1349}
     1350
     1351ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength)
     1352{
     1353    unsigned vectorLength;
     1354    unsigned length;
     1355   
     1356    switch (structure()->indexingType()) {
     1357    case NonArray:
     1358    case Array:
     1359        vectorLength = 0;
     1360        length = 0;
     1361        break;
     1362    case NonArrayWithArrayStorage:
     1363    case ArrayWithArrayStorage:
     1364        vectorLength = m_butterfly->arrayStorage()->vectorLength();
     1365        length = m_butterfly->arrayStorage()->length();
     1366        break;
     1367    default:
     1368        CRASH();
     1369        return 0;
     1370    }
     1371    return getNewVectorLength(vectorLength, length, desiredLength);
     1372}
     1373
     1374bool JSObject::increaseVectorLength(JSGlobalData& globalData, unsigned newLength)
     1375{
     1376    // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
     1377    // to the vector. Callers have to account for that, because they can do it more efficiently.
     1378    if (newLength > MAX_STORAGE_VECTOR_LENGTH)
     1379        return false;
     1380
     1381    ArrayStorage* storage = arrayStorage();
     1382
     1383    unsigned indexBias = storage->m_indexBias;
     1384    unsigned vectorLength = storage->vectorLength();
     1385    ASSERT(newLength > vectorLength);
     1386    unsigned newVectorLength = getNewVectorLength(newLength);
     1387
     1388    // Fast case - there is no precapacity. In these cases a realloc makes sense.
     1389    if (LIKELY(!indexBias)) {
     1390        Butterfly* newButterfly = storage->butterfly()->growArrayRight(globalData, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength));
     1391        if (!newButterfly)
     1392            return false;
     1393        m_butterfly = newButterfly;
     1394        newButterfly->arrayStorage()->setVectorLength(newVectorLength);
     1395        return true;
     1396    }
     1397   
     1398    // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length.
     1399    unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength);
     1400    Butterfly* newButterfly = storage->butterfly()->resizeArray(
     1401        globalData,
     1402        structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength),
     1403        newIndexBias, true, ArrayStorage::sizeFor(newVectorLength));
     1404    if (!newButterfly)
     1405        return false;
     1406   
     1407    m_butterfly = newButterfly;
     1408    newButterfly->arrayStorage()->setVectorLength(newVectorLength);
     1409    newButterfly->arrayStorage()->m_indexBias = newIndexBias;
     1410    return true;
     1411}
     1412
     1413#if CHECK_ARRAY_CONSISTENCY
     1414void JSObject::checkIndexingConsistency(ConsistencyCheckType type)
     1415{
     1416    ArrayStorage* storage = arrayStorageOrNull();
     1417    if (!storage)
     1418        return;
     1419
     1420    ASSERT(!storage->m_inCompactInitialization);
     1421
     1422    ASSERT(storage);
     1423    if (type == SortConsistencyCheck)
     1424        ASSERT(!storage->m_sparseMap);
     1425
     1426    unsigned numValuesInVector = 0;
     1427    for (unsigned i = 0; i < storage->vectorLength(); ++i) {
     1428        if (JSValue value = storage->m_vector[i].get()) {
     1429            ASSERT(i < storage->length());
     1430            if (type != DestructorConsistencyCheck)
     1431                value.isUndefined(); // Likely to crash if the object was deallocated.
     1432            ++numValuesInVector;
     1433        } else {
     1434            if (type == SortConsistencyCheck)
     1435                ASSERT(i >= storage->m_numValuesInVector);
     1436        }
     1437    }
     1438    ASSERT(numValuesInVector == storage->m_numValuesInVector);
     1439    ASSERT(numValuesInVector <= storage->length());
     1440
     1441    if (m_sparseValueMap) {
     1442        SparseArrayValueMap::const_iterator end = m_sparseValueMap->end();
     1443        for (SparseArrayValueMap::const_iterator it = m_sparseValueMap->begin(); it != end; ++it) {
     1444            unsigned index = it->first;
     1445            ASSERT(index < storage->length());
     1446            ASSERT(index >= storage->vectorLength());
     1447            ASSERT(index <= MAX_ARRAY_INDEX);
     1448            ASSERT(it->second);
     1449            if (type != DestructorConsistencyCheck)
     1450                it->second.getNonSparseMode().isUndefined(); // Likely to crash if the object was deallocated.
     1451        }
     1452    }
     1453}
     1454#endif // CHECK_ARRAY_CONSISTENCY
     1455
     1456Butterfly* JSObject::growOutOfLineStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
    6071457{
    6081458    ASSERT(newSize > oldSize);
    6091459
    610     // It's important that this function not rely on structure(), since
    611     // we might be in the middle of a transition.
    612    
    613     PropertyStorage oldPropertyStorage = m_outOfLineStorage.get();
    614     PropertyStorage newPropertyStorage = 0;
    615 
    616     // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
    617     void* temp = newPropertyStorage;
    618     if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp))
    619         CRASH();
    620     newPropertyStorage = static_cast<PropertyStorage>(temp) + newSize + 1;
    621    
    622     memcpy(newPropertyStorage - oldSize - 1, oldPropertyStorage - oldSize - 1, sizeof(WriteBarrierBase<Unknown>) * oldSize);
    623 
    624     ASSERT(newPropertyStorage);
    625     return newPropertyStorage;
     1460    // It's important that this function not rely on structure(), for the property
     1461    // capacity, since we might have already mutated the structure in-place.
     1462   
     1463    return m_butterfly->growPropertyStorage(globalData, structure(), oldSize, newSize);
    6261464}
    6271465
     
    6311469    JSCell* cell = 0;
    6321470    PropertyOffset offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell);
    633     if (offset == invalidOffset)
     1471    if (isValidOffset(offset)) {
     1472        descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
     1473        return true;
     1474    }
     1475   
     1476    unsigned i = propertyName.asIndex();
     1477    if (i == PropertyName::NotAnIndex)
    6341478        return false;
    635     descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
    636     return true;
     1479   
     1480    switch (object->structure()->indexingType()) {
     1481    case NonArray:
     1482    case Array:
     1483        return false;
     1484       
     1485    case NonArrayWithArrayStorage:
     1486    case ArrayWithArrayStorage: {
     1487        ArrayStorage* storage = object->m_butterfly->arrayStorage();
     1488        if (i >= storage->length())
     1489            return false;
     1490        if (i < storage->vectorLength()) {
     1491            WriteBarrier<Unknown>& value = storage->m_vector[i];
     1492            if (!value)
     1493                return false;
     1494            descriptor.setDescriptor(value.get(), 0);
     1495            return true;
     1496        }
     1497        if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
     1498            SparseArrayValueMap::iterator it = map->find(i);
     1499            if (it == map->notFound())
     1500                return false;
     1501            it->second.get(descriptor);
     1502            return true;
     1503        }
     1504        return false;
     1505    }
     1506       
     1507    default:
     1508        ASSERT_NOT_REACHED();
     1509        return false;
     1510    }
    6371511}
    6381512
     
    6591533            if (oldDescriptor.setterPresent())
    6601534                accessor->setSetter(exec->globalData(), oldDescriptor.setterObject());
    661             target->putDirectAccessor(exec->globalData(), propertyName, accessor, attributes | Accessor);
     1535            target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
    6621536            return true;
    6631537        }
     
    6841558        accessor->setSetter(exec->globalData(), oldDescriptor.setterObject());
    6851559
    686     target->putDirectAccessor(exec->globalData(), propertyName, accessor, attributes | Accessor);
     1560    target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
    6871561    return true;
     1562}
     1563
     1564void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
     1565{
     1566    unsigned asIndex = propertyName.asIndex();
     1567    if (asIndex == PropertyName::NotAnIndex)
     1568        putDirect(exec->globalData(), propertyName, value);
     1569    else
     1570        putDirectIndex(exec, asIndex, value);
    6881571}
    6891572
     
    7051588};
    7061589
    707 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
     1590bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
    7081591{
    7091592    // Track on the globaldata that we're in define property.
     
    7121595    // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
    7131596    DefineOwnPropertyScope scope(exec);
    714 
     1597   
    7151598    // If we have a new property we can just put it on normally
    7161599    PropertyDescriptor current;
    717     if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {
     1600    if (!methodTable()->getOwnPropertyDescriptor(this, exec, propertyName, current)) {
    7181601        // unless extensions are prevented!
    719         if (!object->isExtensible()) {
     1602        if (!isExtensible()) {
    7201603            if (throwException)
    7211604                throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible.")));
     
    7241607        PropertyDescriptor oldDescriptor;
    7251608        oldDescriptor.setValue(jsUndefined());
    726         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
     1609        return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
    7271610    }
    7281611
     
    7501633    if (descriptor.isGenericDescriptor()) {
    7511634        if (!current.attributesEqual(descriptor)) {
    752             object->methodTable()->deleteProperty(object, exec, propertyName);
    753             return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
     1635            methodTable()->deleteProperty(this, exec, propertyName);
     1636            return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
    7541637        }
    7551638        return true;
     
    7631646            return false;
    7641647        }
    765         object->methodTable()->deleteProperty(object, exec, propertyName);
    766         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
     1648        methodTable()->deleteProperty(this, exec, propertyName);
     1649        return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
    7671650    }
    7681651
     
    7851668        if (current.attributesEqual(descriptor) && !descriptor.value())
    7861669            return true;
    787         object->methodTable()->deleteProperty(object, exec, propertyName);
    788         return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
     1670        methodTable()->deleteProperty(this, exec, propertyName);
     1671        return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
    7891672    }
    7901673
     
    8031686        }
    8041687    }
    805     JSValue accessor = object->getDirect(exec->globalData(), propertyName);
     1688    JSValue accessor = getDirect(exec->globalData(), propertyName);
    8061689    if (!accessor)
    8071690        return false;
     
    8131696    if (current.attributesEqual(descriptor))
    8141697        return true;
    815     object->methodTable()->deleteProperty(object, exec, propertyName);
     1698    methodTable()->deleteProperty(this, exec, propertyName);
    8161699    unsigned attrs = descriptor.attributesOverridingCurrent(current);
    817     object->putDirectAccessor(exec->globalData(), propertyName, getterSetter, attrs | Accessor);
     1700    putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor);
    8181701    return true;
    8191702}
    8201703
     1704bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
     1705{
     1706    // If it's an array index, then use the indexed property storage.
     1707    unsigned index = propertyName.asIndex();
     1708    if (index != PropertyName::NotAnIndex) {
     1709        // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
     1710        // d. Reject if succeeded is false.
     1711        // e. If index >= oldLen
     1712        // e.i. Set oldLenDesc.[[Value]] to index + 1.
     1713        // 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.
     1714        // f. Return true.
     1715        return object->defineOwnIndexedProperty(exec, index, descriptor, throwException);
     1716    }
     1717   
     1718    return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
     1719}
     1720
     1721bool JSObject::getOwnPropertySlotSlow(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
     1722{
     1723    unsigned i = propertyName.asIndex();
     1724    if (i != PropertyName::NotAnIndex)
     1725        return getOwnPropertySlotByIndex(this, exec, i, slot);
     1726    return false;
     1727}
     1728
    8211729JSObject* throwTypeError(ExecState* exec, const String& message)
    8221730{
  • trunk/Source/JavaScriptCore/runtime/JSObject.h

    r128146 r128400  
    22 *  Copyright (C) 1999-2001 Harri Porten ([email protected])
    33 *  Copyright (C) 2001 Peter Kelly ([email protected])
    4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
     4 *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
    55 *
    66 *  This library is free software; you can redistribute it and/or
     
    2525
    2626#include "ArgList.h"
     27#include "ArrayConventions.h"
     28#include "ArrayStorage.h"
     29#include "Butterfly.h"
    2730#include "ClassInfo.h"
    2831#include "CommonIdentifiers.h"
     
    3033#include "JSCell.h"
    3134#include "PropertySlot.h"
     35#include "PropertyStorage.h"
     36#include "PutDirectIndexMode.h"
    3237#include "PutPropertySlot.h"
    3338
    34 #include "StorageBarrier.h"
    3539#include "Structure.h"
    3640#include "JSGlobalData.h"
    3741#include "JSString.h"
     42#include "SparseArrayValueMap.h"
    3843#include <wtf/StdLibExtras.h>
    3944
     
    8085    };
    8186
     87    COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute);
     88    COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute);
     89    COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute);
     90    COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute);
     91    COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute);
     92    COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute);
     93
    8294    class JSFinalObject;
    8395
     
    97109    public:
    98110        typedef JSCell Base;
    99 
     111       
    100112        JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
    101113
     
    121133        bool allowsAccessFrom(ExecState*);
    122134
     135        unsigned getArrayLength() const
     136        {
     137            switch (structure()->indexingType()) {
     138            case NonArray:
     139            case Array:
     140                return 0;
     141            case NonArrayWithArrayStorage:
     142            case ArrayWithArrayStorage:
     143                return m_butterfly->arrayStorage()->length();
     144            default:
     145                ASSERT_NOT_REACHED();
     146                return 0;
     147            }
     148        }
     149       
     150        unsigned getVectorLength()
     151        {
     152            switch (structure()->indexingType()) {
     153            case NonArray:
     154            case Array:
     155                return 0;
     156            case NonArrayWithArrayStorage:
     157            case ArrayWithArrayStorage:
     158                return m_butterfly->arrayStorage()->vectorLength();
     159            default:
     160                ASSERT_NOT_REACHED();
     161                return 0;
     162            }
     163        }
     164       
    123165        JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
    124166        JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
     167       
     168        // This is similar to the putDirect* methods:
     169        //  - the prototype chain is not consulted
     170        //  - accessors are not called.
     171        //  - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).
     172        // This method creates a property with attributes writable, enumerable and configurable all set to true.
     173        bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode)
     174        {
     175            if (!attributes && canSetIndexQuickly(propertyName)) {
     176                setIndexQuickly(exec->globalData(), propertyName, value);
     177                return true;
     178            }
     179            return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode);
     180        }
     181        bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value)
     182        {
     183            return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect);
     184        }
     185
     186        // A non-throwing version of putDirect and putDirectIndex.
     187        void putDirectMayBeIndex(ExecState*, PropertyName, JSValue);
     188       
     189        bool canGetIndexQuickly(unsigned i)
     190        {
     191            switch (structure()->indexingType()) {
     192            case NonArray:
     193            case Array:
     194                return false;
     195            case NonArrayWithArrayStorage:
     196            case ArrayWithArrayStorage:
     197                return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i];
     198            default:
     199                ASSERT_NOT_REACHED();
     200                return false;
     201            }
     202        }
     203       
     204        JSValue getIndexQuickly(unsigned i)
     205        {
     206            switch (structure()->indexingType()) {
     207            case NonArrayWithArrayStorage:
     208            case ArrayWithArrayStorage:
     209                return m_butterfly->arrayStorage()->m_vector[i].get();
     210            default:
     211                ASSERT_NOT_REACHED();
     212                return JSValue();
     213            }
     214        }
     215       
     216        bool canSetIndexQuickly(unsigned i)
     217        {
     218            switch (structure()->indexingType()) {
     219            case NonArray:
     220            case Array:
     221                return false;
     222            case NonArrayWithArrayStorage:
     223            case ArrayWithArrayStorage:
     224                return i < m_butterfly->arrayStorage()->vectorLength();
     225            default:
     226                ASSERT_NOT_REACHED();
     227                return false;
     228            }
     229        }
     230       
     231        void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v)
     232        {
     233            switch (structure()->indexingType()) {
     234            case NonArrayWithArrayStorage:
     235            case ArrayWithArrayStorage: {
     236                WriteBarrier<Unknown>& x = m_butterfly->arrayStorage()->m_vector[i];
     237                if (!x) {
     238                    ArrayStorage* storage = m_butterfly->arrayStorage();
     239                    ++storage->m_numValuesInVector;
     240                    if (i >= storage->length())
     241                        storage->setLength(i + 1);
     242                }
     243                x.set(globalData, this, v);
     244                break;
     245            }
     246            default:
     247                ASSERT_NOT_REACHED();
     248            }
     249        }
     250       
     251        void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
     252        {
     253            switch (structure()->indexingType()) {
     254            case NonArrayWithArrayStorage:
     255            case ArrayWithArrayStorage: {
     256                ArrayStorage* storage = m_butterfly->arrayStorage();
     257#if CHECK_ARRAY_CONSISTENCY
     258                ASSERT(storage->m_inCompactInitialization);
     259                // Check that we are initializing the next index in sequence.
     260                ASSERT(i == storage->m_initializationIndex);
     261                // tryCreateUninitialized set m_numValuesInVector to the initialLength,
     262                // check we do not try to initialize more than this number of properties.
     263                ASSERT(storage->m_initializationIndex < storage->m_numValuesInVector);
     264                storage->m_initializationIndex++;
     265#endif
     266                ASSERT(i < storage->length());
     267                ASSERT(i < storage->m_numValuesInVector);
     268                storage->m_vector[i].set(globalData, this, v);
     269                break;
     270            }
     271            default:
     272                ASSERT_NOT_REACHED();
     273            }
     274        }
     275       
     276        void completeInitialization(unsigned newLength)
     277        {
     278            switch (structure()->indexingType()) {
     279            case NonArrayWithArrayStorage:
     280            case ArrayWithArrayStorage: {
     281                ArrayStorage* storage = m_butterfly->arrayStorage();
     282                // Check that we have initialized as meny properties as we think we have.
     283                UNUSED_PARAM(storage);
     284                ASSERT_UNUSED(newLength, newLength == storage->length());
     285#if CHECK_ARRAY_CONSISTENCY
     286                // Check that the number of propreties initialized matches the initialLength.
     287                ASSERT(storage->m_initializationIndex == m_storage->m_numValuesInVector);
     288                ASSERT(storage->m_inCompactInitialization);
     289                storage->m_inCompactInitialization = false;
     290#endif
     291                break;
     292            }
     293            default:
     294                ASSERT_NOT_REACHED();
     295            }
     296        }
     297       
     298        bool inSparseIndexingMode()
     299        {
     300            switch (structure()->indexingType()) {
     301            case NonArray:
     302            case Array:
     303                return false;
     304            case NonArrayWithArrayStorage:
     305            case ArrayWithArrayStorage:
     306                return m_butterfly->arrayStorage()->inSparseMode();
     307            default:
     308                ASSERT_NOT_REACHED();
     309                return false;
     310            }
     311        }
     312       
     313        void enterDictionaryIndexingMode(JSGlobalData&);
    125314
    126315        // putDirect is effectively an unchecked vesion of 'defineOwnProperty':
     
    128317        //  - accessors are not called.
    129318        //  - attributes will be respected (after the call the property will exist with the given attributes)
     319        //  - the property name is assumed to not be an index.
    130320        JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
    131321        void putDirect(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
    132322        void putDirect(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);
    133323        void putDirectWithoutTransition(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
    134         void putDirectAccessor(JSGlobalData&, PropertyName, JSValue, unsigned attributes);
     324        void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes);
    135325
    136326        bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
     
    148338
    149339        JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     340        JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    150341        JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    151342
     
    204395        }
    205396       
    206         ConstPropertyStorage outOfLineStorage() const { return m_outOfLineStorage.get(); }
    207         PropertyStorage outOfLineStorage() { return m_outOfLineStorage.get(); }
     397        const Butterfly* butterfly() const { return m_butterfly; }
     398        Butterfly* butterfly() { return m_butterfly; }
     399       
     400        ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); }
     401        PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); }
    208402
    209403        const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
     
    228422                result = offsetInInlineStorage;
    229423            else
    230                 result = outOfLineStorage() - location + (inlineStorageCapacity - 2);
     424                result = outOfLineStorage() - location + (inlineStorageCapacity - 1);
    231425            validateOffset(result, structure()->typeInfo().type());
    232426            return result;
     
    266460        bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); }
    267461        bool isExtensible() { return structure()->isExtensible(); }
     462        bool indexingShouldBeSparse()
     463        {
     464            return !isExtensible()
     465                || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
     466        }
    268467
    269468        bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
    270469        void reifyStaticFunctionsForDelete(ExecState* exec);
    271470
    272         JS_EXPORT_PRIVATE PropertyStorage growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
    273         void setOutOfLineStorage(JSGlobalData&, PropertyStorage, Structure*);
     471        JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
     472        void setButterfly(JSGlobalData&, Butterfly*, Structure*);
     473        void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this.
    274474       
    275475        void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
    276476        void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*);
    277 
    278         void* addressOfOutOfLineStorage()
    279         {
    280             return &m_outOfLineStorage;
    281         }
    282477
    283478        void flattenDictionaryObject(JSGlobalData& globalData)
     
    294489       
    295490        static size_t offsetOfInlineStorage();
    296         static size_t offsetOfOutOfLineStorage();
     491       
     492        static ptrdiff_t butterflyOffset()
     493        {
     494            return OBJECT_OFFSETOF(JSObject, m_butterfly);
     495        }
     496       
     497        void* butterflyAddress()
     498        {
     499            return &m_butterfly;
     500        }
    297501
    298502        static JS_EXPORTDATA const ClassInfo s_info;
     
    317521        // To instantiate objects you likely want JSFinalObject, below.
    318522        // To create derived types you likely want JSNonFinalObject, below.
    319         JSObject(JSGlobalData&, Structure*);
     523        JSObject(JSGlobalData&, Structure*, Butterfly* = 0);
    320524       
    321525        void resetInheritorID(JSGlobalData& globalData)
     
    324528        }
    325529       
    326         void visitOutOfLineStorage(SlotVisitor&, PropertyStorage, size_t storageSize);
     530        void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize);
     531
     532        // Call this if you know that the object is in a mode where it has array
     533        // storage. This will assert otherwise.
     534        ArrayStorage* arrayStorage()
     535        {
     536            ASSERT(structure()->indexingType() | HasArrayStorage);
     537            return m_butterfly->arrayStorage();
     538        }
     539       
     540        // Call this if you want to predicate some actions on whether or not the
     541        // object is in a mode where it has array storage.
     542        ArrayStorage* arrayStorageOrNull()
     543        {
     544            switch (structure()->indexingType()) {
     545            case ArrayWithArrayStorage:
     546            case NonArrayWithArrayStorage:
     547                return m_butterfly->arrayStorage();
     548               
     549            default:
     550                return 0;
     551            }
     552        }
     553
     554        // Ensure that the object is in a mode where it has array storage. Use
     555        // this if you're about to perform actions that would have required the
     556        // object to be converted to have array storage, if it didn't have it
     557        // already.
     558        ArrayStorage* ensureArrayStorage(JSGlobalData& globalData)
     559        {
     560            switch (structure()->indexingType()) {
     561            case ArrayWithArrayStorage:
     562            case NonArrayWithArrayStorage:
     563                return m_butterfly->arrayStorage();
     564               
     565            case NonArray:
     566            case Array:
     567                return createInitialArrayStorage(globalData);
     568               
     569            default:
     570                ASSERT_NOT_REACHED();
     571                return 0;
     572            }
     573        }
     574       
     575        ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength);
     576        ArrayStorage* createInitialArrayStorage(JSGlobalData&);
     577       
     578        ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&);
     579       
     580        bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException);
     581
     582        enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };
     583#if !CHECK_ARRAY_CONSISTENCY
     584        void checkIndexingConsistency(ConsistencyCheckType = NormalConsistencyCheck) { }
     585#else
     586        void checkIndexingConsistency(ConsistencyCheckType = NormalConsistencyCheck);
     587#endif
     588       
     589        void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*);
     590
     591        bool increaseVectorLength(JSGlobalData&, unsigned newLength);
     592        void deallocateSparseIndexMap();
     593        bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
     594        SparseArrayValueMap* allocateSparseIndexMap(JSGlobalData&);
    327595
    328596    private:
     
    337605        void isString();
    338606       
     607        ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*);
     608       
    339609        template<PutMode>
    340610        bool putDirectInternal(JSGlobalData&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*);
    341611
    342612        bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&);
    343         JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
     613        JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, PropertyOffset);
    344614
    345615        const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const;
    346616        Structure* createInheritorID(JSGlobalData&);
    347 
    348         StorageBarrier m_outOfLineStorage;
     617       
     618        void putIndexedDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
     619       
     620        void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
     621        bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*);
     622        JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode);
     623       
     624        unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength);
     625        unsigned getNewVectorLength(unsigned desiredLength);
     626
     627        JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&);
     628       
     629    protected:
     630        Butterfly* m_butterfly;
    349631    };
    350632
     
    370652
    371653    protected:
    372         explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
    373             : JSObject(globalData, structure)
     654        explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly = 0)
     655            : JSObject(globalData, structure, butterfly)
    374656        {
    375657        }
     
    454736}
    455737
    456 inline size_t JSObject::offsetOfOutOfLineStorage()
    457 {
    458     return OBJECT_OFFSETOF(JSObject, m_outOfLineStorage);
    459 }
    460 
    461738inline bool JSObject::isGlobalObject() const
    462739{
     
    489766}
    490767
    491 inline void JSObject::setOutOfLineStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure)
     768inline void JSObject::setButterfly(JSGlobalData& globalData, Butterfly* butterfly, Structure* structure)
    492769{
    493770    ASSERT(structure);
    494     if (!storage) {
    495         ASSERT(!structure->outOfLineCapacity());
    496         ASSERT(!structure->outOfLineSize());
    497     } else {
    498         ASSERT(structure->outOfLineCapacity());
    499         ASSERT(structure->outOfLineSize());
    500     }
     771    ASSERT(!butterfly == (!structure->outOfLineCapacity() && !hasIndexingHeader(structure->indexingType())));
    501772    setStructure(globalData, structure);
    502     m_outOfLineStorage.set(globalData, this, storage);
     773    m_butterfly = butterfly;
     774}
     775
     776inline void JSObject::setButterflyWithoutChangingStructure(Butterfly* butterfly)
     777{
     778    m_butterfly = butterfly;
    503779}
    504780
     
    538814}
    539815
    540 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure)
     816inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly)
    541817    : JSCell(globalData, structure)
    542     , m_outOfLineStorage(globalData, this, 0)
     818    , m_butterfly(butterfly)
    543819{
    544820}
     
    588864ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
    589865{
    590     if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
    591         if (structure()->hasGetterSetterProperties() && location->isGetterSetter())
    592             fillGetterPropertySlot(slot, location);
     866    PropertyOffset offset = structure()->get(exec->globalData(), propertyName);
     867    if (LIKELY(isValidOffset(offset))) {
     868        JSValue value = getDirectOffset(offset);
     869        if (structure()->hasGetterSetterProperties() && value.isGetterSetter())
     870            fillGetterPropertySlot(slot, offset);
    593871        else
    594             slot.setValue(this, location->get(), offsetForLocation(location));
     872            slot.setValue(this, value, offset);
    595873        return true;
    596874    }
    597875
    598     return false;
     876    return getOwnPropertySlotSlow(exec, propertyName, slot);
    599877}
    600878
     
    682960    ASSERT(value.isGetterSetter() == !!(attributes & Accessor));
    683961    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
     962    ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex);
    684963
    685964    if (structure()->isDictionary()) {
     
    710989            return false;
    711990
    712         PropertyStorage newStorage = outOfLineStorage();
     991        Butterfly* newButterfly = m_butterfly;
    713992        if (structure()->putWillGrowOutOfLineStorage())
    714             newStorage = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
     993            newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
    715994        offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
    716         setOutOfLineStorage(globalData, newStorage, structure());
     995        setButterfly(globalData, newButterfly, structure());
    717996
    718997        validateOffset(offset);
     
    7281007    size_t currentCapacity = structure()->outOfLineCapacity();
    7291008    if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) {
    730         PropertyStorage newStorage = outOfLineStorage();
     1009        Butterfly* newButterfly = m_butterfly;
    7311010        if (currentCapacity != structure->outOfLineCapacity())
    732             newStorage = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());
     1011            newButterfly = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());
    7331012
    7341013        validateOffset(offset);
    7351014        ASSERT(structure->isValidOffset(offset));
    736         setOutOfLineStorage(globalData, newStorage, structure);
     1015        setButterfly(globalData, newButterfly, structure);
    7371016        putDirectOffset(globalData, offset, value);
    7381017        // This is a new property; transitions with specific values are not currently cachable,
     
    8011080    }
    8021081   
    803     PropertyStorage newStorage = growOutOfLineStorage(
     1082    Butterfly* newButterfly = growOutOfLineStorage(
    8041083        globalData, oldCapacity, newStructure->outOfLineCapacity());
    805     setOutOfLineStorage(globalData, newStorage, newStructure);
     1084    setButterfly(globalData, newButterfly, newStructure);
    8061085}
    8071086
     
    8371116{
    8381117    ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
    839     PropertyStorage newStorage = outOfLineStorage();
     1118    Butterfly* newButterfly = m_butterfly;
    8401119    if (structure()->putWillGrowOutOfLineStorage())
    841         newStorage = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
     1120        newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
    8421121    PropertyOffset offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value));
    843     setOutOfLineStorage(globalData, newStorage, structure());
     1122    setButterfly(globalData, newButterfly, structure());
    8441123    putDirectOffset(globalData, offset, value);
    8451124}
     
    9331212}
    9341213
     1214inline size_t offsetInButterfly(PropertyOffset offset)
     1215{
     1216    return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
     1217}
     1218
    9351219// This is a helper for patching code where you want to emit a load or store and
    9361220// the base is:
     
    9401224{
    9411225    if (isOutOfLineOffset(offset))
    942         return sizeof(EncodedJSValue) * offsetInOutOfLineStorage(offset);
    943     return JSObject::offsetOfInlineStorage() - JSObject::offsetOfOutOfLineStorage() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);
     1226        return sizeof(EncodedJSValue) * offsetInButterfly(offset);
     1227    return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);
    9441228}
    9451229
     
    9471231{
    9481232    if (isOutOfLineOffset(offset))
    949         return offsetInOutOfLineStorage(offset);
     1233        return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
    9501234    ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue)));
    9511235    return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset);
     
    9551239{
    9561240    if (isOutOfLineOffset(offset))
    957         return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue);
     1241        return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage();
    9581242    return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue);
    9591243}
  • trunk/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp

    r126721 r128400  
    6868        return jsPropertyNameIterator;
    6969   
     70    if (hasIndexingHeader(o->structure()->indexingType()))
     71        return jsPropertyNameIterator;
     72   
    7073    size_t count = normalizePrototypeChain(exec, o);
    7174    StructureChain* structureChain = o->structure()->prototypeChain(exec);
  • trunk/Source/JavaScriptCore/runtime/JSSymbolTableObject.cpp

    r126926 r128400  
    5757}
    5858
    59 void JSSymbolTableObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
     59void JSSymbolTableObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    6060{
    6161    JSSymbolTableObject* thisObject = jsCast<JSSymbolTableObject*>(object);
     
    6666    }
    6767   
    68     JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
     68    JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
    6969}
    7070
  • trunk/Source/JavaScriptCore/runtime/JSSymbolTableObject.h

    r128260 r128400  
    4545   
    4646    JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
    47     JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     47    JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    4848   
    4949protected:
  • trunk/Source/JavaScriptCore/runtime/JSTypeInfo.h

    r117859 r128400  
    4343    static const unsigned IsEnvironmentRecord = 1 << 4;
    4444    static const unsigned OverridesGetOwnPropertySlot = 1 << 5;
    45     static const unsigned OverridesVisitChildren = 1 << 6;
    46     static const unsigned OverridesGetPropertyNames = 1 << 7;
    47     static const unsigned ProhibitsPropertyCaching = 1 << 8;
     45    static const unsigned InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero = 1 << 6;
     46    static const unsigned OverridesVisitChildren = 1 << 7;
     47    static const unsigned OverridesGetPropertyNames = 1 << 8;
     48    static const unsigned ProhibitsPropertyCaching = 1 << 9;
    4849
    4950    class TypeInfo {
     
    7677        bool implementsDefaultHasInstance() const { return isSetOnFlags1(ImplementsDefaultHasInstance); }
    7778        bool overridesGetOwnPropertySlot() const { return isSetOnFlags1(OverridesGetOwnPropertySlot); }
     79        bool interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero() const { return isSetOnFlags1(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero); }
    7880        bool overridesVisitChildren() const { return isSetOnFlags1(OverridesVisitChildren); }
    79         bool overridesGetPropertyNames() const { return isSetOnFlags1(OverridesGetPropertyNames); }
     81        bool overridesGetPropertyNames() const { return isSetOnFlags2(OverridesGetPropertyNames); }
    8082        bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); }
    8183
  • trunk/Source/JavaScriptCore/runtime/LiteralParser.cpp

    r127958 r128400  
    2828#include "LiteralParser.h"
    2929
     30#include "ButterflyInlineMethods.h"
     31#include "CopiedSpaceInlineMethods.h"
    3032#include "JSArray.h"
    3133#include "JSString.h"
     
    641643            case DoParseObjectEndExpression:
    642644            {
    643                 asObject(objectStack.last())->putDirect(m_exec->globalData(), identifierStack.last(), lastValue);
     645                JSObject* object = asObject(objectStack.last());
     646                PropertyName ident = identifierStack.last();
     647                unsigned i = ident.asIndex();
     648                if (i != PropertyName::NotAnIndex)
     649                    object->putDirectIndex(m_exec, i, lastValue);
     650                else
     651                    object->putDirect(m_exec->globalData(), ident, lastValue);
    644652                identifierStack.removeLast();
    645653                if (m_lexer.currentToken().type == TokComma)
  • trunk/Source/JavaScriptCore/runtime/ObjectConstructor.cpp

    r127958 r128400  
    2222#include "ObjectConstructor.h"
    2323
     24#include "ButterflyInlineMethods.h"
     25#include "CopiedSpaceInlineMethods.h"
    2426#include "Error.h"
    2527#include "ExceptionHelpers.h"
  • trunk/Source/JavaScriptCore/runtime/ObjectPrototype.cpp

    r127930 r128400  
    6868ObjectPrototype::ObjectPrototype(ExecState* exec, Structure* stucture)
    6969    : JSNonFinalObject(exec->globalData(), stucture)
    70     , m_hasNoPropertiesWithUInt32Names(true)
    7170{
    7271}
     
    7675    Base::finishCreation(globalData);
    7776    ASSERT(inherits(&s_info));
    78 }
    79 
    80 void ObjectPrototype::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
    81 {
    82     ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell);
    83     Base::put(cell, exec, propertyName, value, slot);
    84 
    85     if (thisObject->m_hasNoPropertiesWithUInt32Names && propertyName.asIndex() != PropertyName::NotAnIndex)
    86         thisObject->m_hasNoPropertiesWithUInt32Names = false;
    87 }
    88 
    89 bool ObjectPrototype::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
    90 {
    91     ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(object);
    92     bool result = Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
    93 
    94     if (thisObject->m_hasNoPropertiesWithUInt32Names && propertyName.asIndex() != PropertyName::NotAnIndex)
    95         thisObject->m_hasNoPropertiesWithUInt32Names = false;
    96 
    97     return result;
    98 }
    99 
    100 bool ObjectPrototype::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
    101 {
    102     ObjectPrototype* thisObject = jsCast<ObjectPrototype*>(cell);
    103     if (thisObject->m_hasNoPropertiesWithUInt32Names)
    104         return false;
    105     return Base::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot);
    10677}
    10778
  • trunk/Source/JavaScriptCore/runtime/ObjectPrototype.h

    r116828 r128400  
    5151    private:
    5252        ObjectPrototype(ExecState*, Structure*);
    53         static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
    54         static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
    55 
    5653        static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
    57         static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
    5854        static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
    59 
    60         bool m_hasNoPropertiesWithUInt32Names;
    6155    };
    6256
  • trunk/Source/JavaScriptCore/runtime/PropertyOffset.h

    r128146 r128400  
    119119    validateOffset(offset);
    120120    ASSERT(isOutOfLineOffset(offset));
    121     return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 2;
     121    return -static_cast<ptrdiff_t>(offset - firstOutOfLineOffset) - 1;
    122122}
    123123
  • trunk/Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp

    r127349 r128400  
    2727#include "RegExpMatchesArray.h"
    2828
     29#include "ButterflyInlineMethods.h"
     30#include "SparseArrayValueMapInlineMethods.h"
     31
    2932namespace JSC {
    3033
     
    3336const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, CREATE_METHOD_TABLE(RegExpMatchesArray)};
    3437
     38RegExpMatchesArray::RegExpMatchesArray(JSGlobalData& globalData, Butterfly* butterfly, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result)
     39    : JSArray(globalData, globalObject->regExpMatchesArrayStructure(), butterfly)
     40    , m_result(result)
     41    , m_state(ReifiedNone)
     42{
     43    m_input.set(globalData, this, input);
     44    m_regExp.set(globalData, this, regExp);
     45}
     46
     47RegExpMatchesArray* RegExpMatchesArray::create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result)
     48{
     49    ASSERT(result);
     50    JSGlobalData& globalData = exec->globalData();
     51    Butterfly* butterfly = createArrayButterfly(globalData, regExp->numSubpatterns() + 1);
     52    RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, butterfly, exec->lexicalGlobalObject(), input, regExp, result);
     53    array->finishCreation(globalData);
     54    return array;
     55}
     56
    3557void RegExpMatchesArray::finishCreation(JSGlobalData& globalData)
    3658{
    37     Base::finishCreation(globalData, m_regExp->numSubpatterns() + 1);
     59    Base::finishCreation(globalData);
    3860}
    3961
  • trunk/Source/JavaScriptCore/runtime/RegExpMatchesArray.h

    r116828 r128400  
    2929    class RegExpMatchesArray : public JSArray {
    3030    private:
    31         RegExpMatchesArray(JSGlobalData& globalData, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result)
    32             : JSArray(globalData, globalObject->regExpMatchesArrayStructure())
    33             , m_result(result)
    34             , m_state(ReifiedNone)
    35         {
    36             m_input.set(globalData, this, input);
    37             m_regExp.set(globalData, this, regExp);
    38         }
     31        RegExpMatchesArray(JSGlobalData&, Butterfly*, JSGlobalObject*, JSString*, RegExp*, MatchResult);
    3932
    4033        enum ReifiedState { ReifiedNone, ReifiedMatch, ReifiedAll };
     
    4336        typedef JSArray Base;
    4437
    45         static RegExpMatchesArray* create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result)
    46         {
    47             ASSERT(result);
    48             JSGlobalData& globalData = exec->globalData();
    49             RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, exec->lexicalGlobalObject(), input, regExp, result);
    50             array->finishCreation(globalData);
    51             return array;
    52         }
     38        static RegExpMatchesArray* create(ExecState*, JSString*, RegExp*, MatchResult);
    5339
    5440        JSString* leftContext(ExecState*);
     
    5945        static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
    6046        {
    61             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
     47            return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info, ArrayWithArrayStorage);
    6248        }
    6349
  • trunk/Source/JavaScriptCore/runtime/RegExpObject.cpp

    r127505 r128400  
    2222#include "RegExpObject.h"
    2323
     24#include "ButterflyInlineMethods.h"
     25#include "CopiedSpaceInlineMethods.h"
    2426#include "Error.h"
    2527#include "ExceptionHelpers.h"
     
    114116}
    115117
    116 void RegExpObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
     118void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    117119{
    118120    if (mode == IncludeDontEnumProperties)
    119121        propertyNames.add(exec->propertyNames().lastIndex);
    120     Base::getOwnPropertyNames(object, exec, propertyNames, mode);
     122    Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
    121123}
    122124
  • trunk/Source/JavaScriptCore/runtime/RegExpObject.h

    r116828 r128400  
    9191
    9292        JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
    93         JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     93        JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    9494        JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    9595        JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
  • trunk/Source/JavaScriptCore/runtime/StringObject.cpp

    r127505 r128400  
    7979}
    8080
     81void StringObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
     82{
     83    StringObject* thisObject = jsCast<StringObject*>(cell);
     84    if (thisObject->internalValue()->canGetIndex(propertyName)) {
     85        if (shouldThrow)
     86            throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
     87        return;
     88    }
     89    JSObject::putByIndex(cell, exec, propertyName, value, shouldThrow);
     90}
     91
    8192bool StringObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
    8293{
     
    134145}
    135146
     147bool StringObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
     148{
     149    StringObject* thisObject = jsCast<StringObject*>(cell);
     150    if (thisObject->internalValue()->canGetIndex(i))
     151        return false;
     152    return JSObject::deletePropertyByIndex(thisObject, exec, i);
     153}
     154
    136155void StringObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    137156{
  • trunk/Source/JavaScriptCore/runtime/StringObject.h

    r126464 r128400  
    5151
    5252        static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
     53        static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
    5354
    5455        static bool deleteProperty(JSCell*, ExecState*, PropertyName);
     56        static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
    5557        static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    5658        static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
     
    6769    protected:
    6870        JS_EXPORT_PRIVATE void finishCreation(JSGlobalData&, JSString*);
    69         static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesGetPropertyNames | JSWrapperObject::StructureFlags;
     71        static const unsigned StructureFlags = OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | JSWrapperObject::StructureFlags;
    7072        JS_EXPORT_PRIVATE StringObject(JSGlobalData&, Structure*);
    7173    };
  • trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp

    r127505 r128400  
    2323#include "StringPrototype.h"
    2424
     25#include "ButterflyInlineMethods.h"
    2526#include "CachedCall.h"
     27#include "CopiedSpaceInlineMethods.h"
    2628#include "Error.h"
    2729#include "Executable.h"
  • trunk/Source/JavaScriptCore/runtime/Structure.cpp

    r126721 r128400  
    150150}
    151151
    152 Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo)
     152Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType)
    153153    : JSCell(globalData, globalData.structureStructure.get())
    154154    , m_typeInfo(typeInfo)
     155    , m_indexingType(indexingType)
    155156    , m_globalObject(globalData, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull)
    156157    , m_prototype(globalData, this, prototype)
     
    177178    : JSCell(CreatingEarlyCell)
    178179    , m_typeInfo(CompoundType, OverridesVisitChildren)
     180    , m_indexingType(0)
    179181    , m_prototype(globalData, this, jsNull())
    180182    , m_classInfo(&s_info)
     
    198200    : JSCell(globalData, globalData.structureStructure.get())
    199201    , m_typeInfo(previous->typeInfo())
     202    , m_indexingType(previous->indexingTypeIncludingHistory())
    200203    , m_prototype(globalData, this, previous->storedPrototype())
    201204    , m_classInfo(previous->m_classInfo)
     
    252255    for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
    253256        structure = structures[i];
     257        if (!structure->m_nameInPrevious)
     258            continue;
    254259        PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get());
    255260        m_propertyTable->add(entry);
     
    500505    transition->pin();
    501506
     507    return transition;
     508}
     509
     510Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* structure, NonPropertyTransition transitionKind)
     511{
     512    unsigned attributes = toAttributes(transitionKind);
     513    IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind);
     514   
     515    if (Structure* existingTransition = structure->m_transitionTable.get(0, attributes)) {
     516        ASSERT(existingTransition->m_attributesInPrevious == attributes);
     517        ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType);
     518        return existingTransition;
     519    }
     520   
     521    Structure* transition = create(globalData, structure);
     522    transition->m_previous.set(globalData, transition, structure);
     523    transition->m_attributesInPrevious = attributes;
     524    transition->m_indexingType = indexingType;
     525   
     526    if (structure->m_propertyTable) {
     527        if (structure->m_isPinnedPropertyTable)
     528            transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1);
     529        else
     530            transition->m_propertyTable = structure->m_propertyTable.release();
     531    } else {
     532        if (structure->m_previous)
     533            transition->materializePropertyMap(globalData);
     534        else
     535            transition->createPropertyMap();
     536    }
     537   
     538    structure->m_transitionTable.add(globalData, transition);
    502539    return transition;
    503540}
  • trunk/Source/JavaScriptCore/runtime/Structure.h

    r128146 r128400  
    2828
    2929#include "ClassInfo.h"
     30#include "IndexingType.h"
    3031#include "JSCell.h"
    3132#include "JSType.h"
     
    6970        typedef JSCell Base;
    7071
    71         static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*);
     72        static Structure* create(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0);
    7273
    7374    protected:
     
    101102        static Structure* freezeTransition(JSGlobalData&, Structure*);
    102103        static Structure* preventExtensionsTransition(JSGlobalData&, Structure*);
     104        static Structure* nonPropertyTransition(JSGlobalData&, Structure*, NonPropertyTransition);
    103105
    104106        bool isSealed(JSGlobalData&);
     
    141143        bool isObject() const { return typeInfo().isObject(); }
    142144
     145        IndexingType indexingType() const { return m_indexingType & AllArrayTypes; }
     146        IndexingType indexingTypeIncludingHistory() const { return m_indexingType; }
    143147
    144148        JSGlobalObject* globalObject() const { return m_globalObject.get(); }
     
    335339            return OBJECT_OFFSETOF(Structure, m_classInfo);
    336340        }
     341       
     342        static ptrdiff_t indexingTypeOffset()
     343        {
     344            return OBJECT_OFFSETOF(Structure, m_indexingType);
     345        }
    337346
    338347        static Structure* createStructure(JSGlobalData&);
     
    364373        friend class LLIntOffsetsExtractor;
    365374
    366         JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*);
     375        JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = 0);
    367376        Structure(JSGlobalData&);
    368377        Structure(JSGlobalData&, const Structure*);
     
    417426
    418427        TypeInfo m_typeInfo;
     428        IndexingType m_indexingType;
    419429       
    420430        WriteBarrier<JSGlobalObject> m_globalObject;
     
    448458        bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
    449459        bool m_hasNonEnumerableProperties : 1;
    450         unsigned m_attributesInPrevious : 7;
     460        unsigned m_attributesInPrevious : 22;
    451461        unsigned m_specificFunctionThrashCount : 2;
    452462        unsigned m_preventExtensions : 1;
     
    466476    }
    467477
    468     inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo)
     478    inline Structure* Structure::create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType)
    469479    {
    470480        ASSERT(globalData.structureStructure);
    471481        ASSERT(classInfo);
    472         Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo);
     482        Structure* structure = new (NotNull, allocateCell<Structure>(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo, indexingType);
    473483        structure->finishCreation(globalData);
    474484        return structure;
  • trunk/Source/JavaScriptCore/runtime/StructureTransitionTable.h

    r127191 r128400  
    2727#define StructureTransitionTable_h
    2828
     29#include "IndexingType.h"
    2930#include "WeakGCMap.h"
    3031#include <wtf/HashFunctions.h>
     
    3839class Structure;
    3940
     41static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions that don't have to do with property additions.
     42
     43// Support for attributes used to indicate transitions not related to properties.
     44// If any of these are used, the string portion of the key should be 0.
     45enum NonPropertyTransition {
     46    AllocateArrayStorage
     47};
     48
     49inline unsigned toAttributes(NonPropertyTransition transition)
     50{
     51    return transition + FirstInternalAttribute;
     52}
     53
     54inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition)
     55{
     56    switch (transition) {
     57    case AllocateArrayStorage:
     58        return oldType | HasArrayStorage;
     59    default:
     60        ASSERT_NOT_REACHED();
     61        return oldType;
     62    }
     63}
     64
    4065class StructureTransitionTable {
    4166    static const intptr_t UsingSingleSlotFlag = 1;
     
    4570        static unsigned hash(const Key& p)
    4671        {
    47             return p.first->existingHash();
     72            unsigned result = p.second;
     73            if (p.first)
     74                result += p.first->existingHash();
     75            return result;
    4876        }
    4977
Note: See TracChangeset for help on using the changeset viewer.