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

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

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

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

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

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

  • API/JSCallbackObject.h:

(JSCallbackObject):

  • API/JSCallbackObjectFunctions.h:

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

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

(JSC):
(JSC::arrayModeFromStructure):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitDirectPutById):

  • dfg/DFGAbstractState.cpp:

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

  • dfg/DFGAdjacencyList.h:

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

  • dfg/DFGArrayMode.cpp:

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

  • dfg/DFGArrayMode.h:

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

  • dfg/DFGByteCodeParser.cpp:

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

  • dfg/DFGCSEPhase.cpp:

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

  • dfg/DFGFixupPhase.cpp:

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

  • dfg/DFGGraph.h:

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

  • dfg/DFGNode.h:

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

  • dfg/DFGNodeType.h:

(DFG):

  • dfg/DFGOperations.cpp:

(JSC::DFG::putByVal):

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

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

  • dfg/DFGRepatch.cpp:

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

  • dfg/DFGSpeculativeJIT.cpp:

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

  • dfg/DFGSpeculativeJIT.h:

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

  • dfg/DFGSpeculativeJIT32_64.cpp:

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

  • dfg/DFGSpeculativeJIT64.cpp:

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

  • dfg/DFGStructureCheckHoistingPhase.cpp:

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

  • heap/CopiedSpace.h:

(CopiedSpace):

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

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

  • jit/JITOpcodes.cpp:

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

  • jit/JITPropertyAccess.cpp:

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

  • jit/JITPropertyAccess32_64.cpp:

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

  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):

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

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

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

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

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

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

  • runtime/ArrayPrototype.cpp:

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

  • runtime/ArrayPrototype.h:

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

  • runtime/ArrayStorage.h: Added.

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

  • runtime/Butterfly.h: Added.

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

  • runtime/ButterflyInlineMethods.h: Added.

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

  • runtime/ClassInfo.h:

(MethodTable):
(JSC):

  • runtime/IndexingHeader.h: Added.

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

  • runtime/IndexingHeaderInlineMethods.h: Added.

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

  • runtime/IndexingType.h: Added.

(JSC):
(JSC::hasIndexingHeader):

  • runtime/JSActivation.cpp:

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

  • runtime/JSActivation.h:

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

  • runtime/JSArray.cpp:

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

  • runtime/JSArray.h:

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

  • runtime/JSBoundFunction.cpp:

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

  • runtime/JSCell.cpp:

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

  • runtime/JSCell.h:

(JSCell):

  • runtime/JSFunction.cpp:

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

  • runtime/JSFunction.h:

(JSFunction):

  • runtime/JSGlobalData.cpp:

(JSC::JSGlobalData::JSGlobalData):

  • runtime/JSGlobalData.h:

(JSGlobalData):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::reset):

  • runtime/JSONObject.cpp:

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

  • runtime/JSObject.cpp:

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

  • runtime/JSObject.h:

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

  • runtime/JSPropertyNameIterator.cpp:

(JSC::JSPropertyNameIterator::create):

  • runtime/JSSymbolTableObject.cpp:

(JSC::JSSymbolTableObject::getOwnNonIndexPropertyNames):

  • runtime/JSSymbolTableObject.h:

(JSSymbolTableObject):

  • runtime/JSTypeInfo.h:

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

  • runtime/LiteralParser.cpp:

(JSC::::parse):

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

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

  • runtime/ObjectPrototype.h:

(ObjectPrototype):

  • runtime/PropertyOffset.h:

(JSC::offsetInOutOfLineStorage):

  • runtime/PropertyStorage.h: Added.

(JSC):

  • runtime/PutDirectIndexMode.h: Added.

(JSC):

  • runtime/RegExpMatchesArray.cpp:

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

  • runtime/RegExpMatchesArray.h:

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

  • runtime/RegExpObject.cpp:

(JSC::RegExpObject::getOwnNonIndexPropertyNames):

  • runtime/RegExpObject.h:

(RegExpObject):

  • runtime/Reject.h: Added.

(JSC):
(JSC::reject):

  • runtime/SparseArrayValueMap.cpp: Added.

(JSC):

  • runtime/SparseArrayValueMap.h: Added.

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

  • runtime/SparseArrayValueMapInlineMethods.h: Added.

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

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

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

  • runtime/StringObject.h:

(StringObject):

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

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

  • runtime/Structure.h:

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

  • runtime/StructureTransitionTable.h:

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

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

Source/WebCore:

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

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

  • bindings/js/ArrayValue.cpp:

(WebCore::ArrayValue::get):

  • bindings/js/JSBlobCustom.cpp:

(WebCore::JSBlobConstructor::constructJSBlob):

  • bindings/js/JSCanvasRenderingContext2DCustom.cpp:

(WebCore::JSCanvasRenderingContext2D::setWebkitLineDash):

  • bindings/js/JSDOMStringListCustom.cpp:

(WebCore::toDOMStringList):

  • bindings/js/JSDOMStringMapCustom.cpp:

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

  • bindings/js/JSDOMWindowCustom.cpp:

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

  • bindings/js/JSDOMWindowShell.cpp:

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

  • bindings/js/JSDOMWindowShell.h:

(JSDOMWindowShell):

  • bindings/js/JSHistoryCustom.cpp:

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

  • bindings/js/JSInspectorFrontendHostCustom.cpp:

(WebCore::populateContextMenuItems):

  • bindings/js/JSLocationCustom.cpp:

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

  • bindings/js/JSStorageCustom.cpp:

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

  • bindings/js/JSWebSocketCustom.cpp:

(WebCore::JSWebSocketConstructor::constructJSWebSocket):

  • bindings/js/ScriptValue.cpp:

(WebCore::jsToInspectorValue):

  • bindings/js/SerializedScriptValue.cpp:

(WebCore::CloneSerializer::serialize):

  • bindings/scripts/CodeGeneratorJS.pm:

(GenerateHeader):
(GenerateImplementation):

  • bridge/runtime_array.cpp:

(JSC::RuntimeArray::RuntimeArray):

  • bridge/runtime_array.h:

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

LayoutTests:

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

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

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

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/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{
Note: See TracChangeset for help on using the changeset viewer.