diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/runtime/Structure.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/runtime/Structure.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/Structure.cpp | 897 |
1 files changed, 340 insertions, 557 deletions
diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp index 62c752cb3..8781ab007 100644 --- a/Source/JavaScriptCore/runtime/Structure.cpp +++ b/Source/JavaScriptCore/runtime/Structure.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,18 +28,13 @@ #include "CodeBlock.h" #include "DumpContext.h" -#include "JSCInlines.h" #include "JSObject.h" -#include "JSPropertyNameEnumerator.h" +#include "JSPropertyNameIterator.h" #include "Lookup.h" -#include "PropertyMapHashTable.h" #include "PropertyNameArray.h" #include "StructureChain.h" #include "StructureRareDataInlines.h" -#include "WeakGCMapInlines.h" #include <wtf/CommaPrinter.h> -#include <wtf/NeverDestroyed.h> -#include <wtf/ProcessID.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/RefPtr.h> #include <wtf/Threading.h> @@ -55,79 +50,53 @@ using namespace std; using namespace WTF; +#if DUMP_PROPERTYMAP_STATS + +int numProbes; +int numCollisions; +int numRehashes; +int numRemoves; + +#endif + namespace JSC { #if DUMP_STRUCTURE_ID_STATISTICS static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); #endif -class SingleSlotTransitionWeakOwner final : public WeakHandleOwner { - void finalize(Handle<Unknown>, void* context) override - { - StructureTransitionTable* table = reinterpret_cast<StructureTransitionTable*>(context); - ASSERT(table->isUsingSingleSlot()); - WeakSet::deallocate(table->weakImpl()); - table->m_data = StructureTransitionTable::UsingSingleSlotFlag; - } -}; - -static SingleSlotTransitionWeakOwner& singleSlotTransitionWeakOwner() -{ - static NeverDestroyed<SingleSlotTransitionWeakOwner> owner; - return owner; -} - -inline Structure* StructureTransitionTable::singleTransition() const -{ - ASSERT(isUsingSingleSlot()); - if (WeakImpl* impl = this->weakImpl()) { - if (impl->state() == WeakImpl::Live) - return jsCast<Structure*>(impl->jsValue().asCell()); - } - return nullptr; -} - -inline void StructureTransitionTable::setSingleTransition(Structure* structure) -{ - ASSERT(isUsingSingleSlot()); - if (WeakImpl* impl = this->weakImpl()) - WeakSet::deallocate(impl); - WeakImpl* impl = WeakSet::allocate(structure, &singleSlotTransitionWeakOwner(), this); - m_data = reinterpret_cast<intptr_t>(impl) | UsingSingleSlotFlag; -} - -bool StructureTransitionTable::contains(UniquedStringImpl* rep, unsigned attributes) const +bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const { if (isUsingSingleSlot()) { Structure* transition = singleTransition(); - return transition && transition->m_nameInPrevious == rep && transition->attributesInPrevious() == attributes; + return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes; } return map()->get(std::make_pair(rep, attributes)); } -Structure* StructureTransitionTable::get(UniquedStringImpl* rep, unsigned attributes) const +inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const { if (isUsingSingleSlot()) { Structure* transition = singleTransition(); - return (transition && transition->m_nameInPrevious == rep && transition->attributesInPrevious() == attributes) ? transition : 0; + return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0; } return map()->get(std::make_pair(rep, attributes)); } -void StructureTransitionTable::add(VM& vm, Structure* structure) +inline void StructureTransitionTable::add(VM& vm, Structure* structure) { if (isUsingSingleSlot()) { Structure* existingTransition = singleTransition(); // This handles the first transition being added. if (!existingTransition) { - setSingleTransition(structure); + setSingleTransition(vm, structure); return; } // This handles the second transition being added // (or the first transition being despecified!) - setMap(new TransitionMap(vm)); + setMap(new TransitionMap()); add(vm, existingTransition); } @@ -136,7 +105,7 @@ void StructureTransitionTable::add(VM& vm, Structure* structure) // Newer versions of the STL have an std::make_pair function that takes rvalue references. // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue. // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details - map()->set(std::make_pair(structure->m_nameInPrevious.get(), +structure->attributesInPrevious()), structure); + map()->set(std::make_pair(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious), structure); } void Structure::dumpStatistics() @@ -186,38 +155,33 @@ void Structure::dumpStatistics() Structure::Structure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) : JSCell(vm, vm.structureStructure.get()) - , m_blob(vm.heap.structureIDTable().allocateID(this), indexingType, typeInfo) - , m_outOfLineTypeFlags(typeInfo.outOfLineTypeFlags()) , m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) , m_prototype(vm, this, prototype) , m_classInfo(classInfo) , m_transitionWatchpointSet(IsWatched) , m_offset(invalidOffset) + , m_typeInfo(typeInfo) + , m_indexingType(indexingType) , m_inlineCapacity(inlineCapacity) - , m_bitField(0) + , m_dictionaryKind(NoneDictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties(vm)) + , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties(vm)) + , m_hasNonEnumerableProperties(false) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(0) + , m_preventExtensions(false) + , m_didTransition(false) + , m_staticFunctionReified(false) { - setDictionaryKind(NoneDictionaryKind); - setIsPinnedPropertyTable(false); - setHasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties()); - setHasCustomGetterSetterProperties(false); - setHasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties()); - setHasNonEnumerableProperties(false); - setAttributesInPrevious(0); - setPreventExtensions(false); - setDidTransition(false); - setStaticFunctionsReified(false); - setHasRareData(false); - setTransitionWatchpointIsLikelyToBeFired(false); - setHasBeenDictionary(false); - ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity()); ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset); - ASSERT(!hasRareData()); - ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); - ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); + ASSERT(!typeInfo.structureHasRareData()); + ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); + ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); } -const ClassInfo Structure::s_info = { "Structure", 0, 0, CREATE_METHOD_TABLE(Structure) }; +const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; Structure::Structure(VM& vm) : JSCell(CreatingEarlyCell) @@ -225,76 +189,54 @@ Structure::Structure(VM& vm) , m_classInfo(info()) , m_transitionWatchpointSet(IsWatched) , m_offset(invalidOffset) + , m_typeInfo(CompoundType, OverridesVisitChildren) + , m_indexingType(0) , m_inlineCapacity(0) - , m_bitField(0) -{ - setDictionaryKind(NoneDictionaryKind); - setIsPinnedPropertyTable(false); - setHasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties()); - setHasCustomGetterSetterProperties(false); - setHasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties()); - setHasNonEnumerableProperties(false); - setAttributesInPrevious(0); - setPreventExtensions(false); - setDidTransition(false); - setStaticFunctionsReified(false); - setHasRareData(false); - setTransitionWatchpointIsLikelyToBeFired(false); - setHasBeenDictionary(false); - - TypeInfo typeInfo = TypeInfo(CellType, StructureFlags); - m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), 0, typeInfo); - m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags(); - - ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); - ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); -} - -Structure::Structure(VM& vm, Structure* previous, DeferredStructureTransitionWatchpointFire* deferred) + , m_dictionaryKind(NoneDictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties(vm)) + , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties(vm)) + , m_hasNonEnumerableProperties(false) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(0) + , m_preventExtensions(false) + , m_didTransition(false) + , m_staticFunctionReified(false) +{ + ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); + ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); +} + +Structure::Structure(VM& vm, const Structure* previous) : JSCell(vm, vm.structureStructure.get()) , m_prototype(vm, this, previous->storedPrototype()) , m_classInfo(previous->m_classInfo) , m_transitionWatchpointSet(IsWatched) , m_offset(invalidOffset) + , m_typeInfo(previous->typeInfo().type(), previous->typeInfo().flags() & ~StructureHasRareData) + , m_indexingType(previous->indexingTypeIncludingHistory()) , m_inlineCapacity(previous->m_inlineCapacity) - , m_bitField(0) -{ - setDictionaryKind(previous->dictionaryKind()); - setIsPinnedPropertyTable(previous->hasBeenFlattenedBefore()); - setHasGetterSetterProperties(previous->hasGetterSetterProperties()); - setHasCustomGetterSetterProperties(previous->hasCustomGetterSetterProperties()); - setHasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->hasReadOnlyOrGetterSetterPropertiesExcludingProto()); - setHasNonEnumerableProperties(previous->hasNonEnumerableProperties()); - setAttributesInPrevious(0); - setPreventExtensions(previous->preventExtensions()); - setDidTransition(true); - setStaticFunctionsReified(previous->staticFunctionsReified()); - setHasRareData(false); - setHasBeenDictionary(previous->hasBeenDictionary()); - - TypeInfo typeInfo = previous->typeInfo(); - m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingTypeIncludingHistory(), typeInfo); - m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags(); - - ASSERT(!previous->typeInfo().structureIsImmortal()); - setPreviousID(vm, previous); - - previous->didTransitionFromThisStructure(deferred); - - // Copy this bit now, in case previous was being watched. - setTransitionWatchpointIsLikelyToBeFired(previous->transitionWatchpointIsLikelyToBeFired()); - + , m_dictionaryKind(previous->m_dictionaryKind) + , m_isPinnedPropertyTable(false) + , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties) + , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto) + , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties) + , m_attributesInPrevious(0) + , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount) + , m_preventExtensions(previous->m_preventExtensions) + , m_didTransition(true) + , m_staticFunctionReified(previous->m_staticFunctionReified) +{ + if (previous->typeInfo().structureHasRareData() && previous->rareData()->needsCloning()) + cloneRareDataFrom(vm, previous); + else if (previous->previousID()) + m_previousOrRareData.set(vm, this, previous->previousID()); + + previous->notifyTransitionFromThisStructure(); if (previous->m_globalObject) m_globalObject.set(vm, this, previous->m_globalObject.get()); - ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); - ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties()); -} - -Structure::~Structure() -{ - if (typeInfo().structureIsImmortal()) - return; - Heap::heap(this)->structureIDTable().deallocateID(this, m_blob.structureID()); + ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); + ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); } void Structure::destroy(JSCell* cell) @@ -337,7 +279,7 @@ void Structure::materializePropertyMap(VM& vm) findStructuresAndMapForMaterialization(structures, structure, table); if (table) { - table = table->copy(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); + table = table->copy(vm, structure, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); structure->m_lock.unlock(); } @@ -350,27 +292,53 @@ void Structure::materializePropertyMap(VM& vm) else propertyTable().set(vm, this, table); - InferredTypeTable* typeTable = m_inferredTypeTable.get(); - for (size_t i = structures.size(); i--;) { structure = structures[i]; if (!structure->m_nameInPrevious) continue; - PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious()); - if (typeTable && typeTable->get(structure->m_nameInPrevious.get())) - entry.hasInferredType = true; + PropertyMapEntry entry(vm, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange); } checkOffsetConsistency(); } -Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset) +inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity) +{ + if (!currentCapacity) + return initialOutOfLineCapacity; + return currentCapacity * outOfLineGrowthFactor; +} + +size_t Structure::suggestedNewOutOfLineStorageCapacity() +{ + return nextOutOfLineStorageCapacity(outOfLineCapacity()); +} + +void Structure::despecifyDictionaryFunction(VM& vm, PropertyName propertyName) +{ + StringImpl* rep = propertyName.uid(); + + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + + ASSERT(isDictionary()); + ASSERT(propertyTable()); + + PropertyMapEntry* entry = propertyTable()->find(rep).first; + ASSERT(entry); + entry->specificValue.clear(); +} + +Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) { ASSERT(!structure->isDictionary()); ASSERT(structure->isObject()); if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) { + JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get(); + if (specificValueInPrevious && specificValueInPrevious != specificValue) + return 0; validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity()); offset = existingTransition->m_offset; return existingTransition; @@ -379,16 +347,16 @@ Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* st return 0; } -Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset) +Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) { ASSERT(!isCompilationThread()); - return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, offset); + return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, specificValue, offset); } -Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset) +Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) { ConcurrentJITLocker locker(structure->m_lock); - return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, offset); + return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, specificValue, offset); } bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const @@ -405,30 +373,6 @@ bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const } } -bool Structure::holesMustForwardToPrototype(VM& vm) const -{ - if (this->mayInterceptIndexedAccesses()) - return true; - - JSValue prototype = this->storedPrototype(); - if (!prototype.isObject()) - return false; - JSObject* object = asObject(prototype); - - while (true) { - Structure& structure = *object->structure(vm); - if (hasIndexedProperties(object->indexingType()) || structure.mayInterceptIndexedAccesses()) - return true; - prototype = structure.storedPrototype(); - if (!prototype.isObject()) - return false; - object = asObject(prototype); - } - - RELEASE_ASSERT_NOT_REACHED(); - return false; -} - bool Structure::needsSlowPutIndexing() const { return anyObjectInChainMayInterceptIndexedAccesses() @@ -443,34 +387,48 @@ NonPropertyTransition Structure::suggestedArrayStorageTransition() const return AllocateArrayStorage; } -Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset, PutPropertySlot::Context context, DeferredStructureTransitionWatchpointFire* deferred) +Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset, PutPropertySlot::Context context) { + // If we have a specific function, we may have got to this point if there is + // already a transition with the correct property name and attributes, but + // specialized to a different function. In this case we just want to give up + // and despecialize the transition. + // In this case we clear the value of specificFunction which will result + // in us adding a non-specific transition, and any subsequent lookup in + // Structure::addPropertyTransitionToExistingStructure will just use that. + if (specificValue && structure->m_transitionTable.contains(propertyName.uid(), attributes)) + specificValue = 0; + ASSERT(!structure->isDictionary()); ASSERT(structure->isObject()); - ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset)); + ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); + if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; + int maxTransitionLength; if (context == PutPropertySlot::PutById) maxTransitionLength = s_maxTransitionLengthForNonEvalPutById; else maxTransitionLength = s_maxTransitionLength; if (structure->transitionCount() > maxTransitionLength) { - Structure* transition = toCacheableDictionaryTransition(vm, structure, deferred); + Structure* transition = toCacheableDictionaryTransition(vm, structure); ASSERT(structure != transition); - offset = transition->add(vm, propertyName, attributes); + offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); return transition; } - Structure* transition = create(vm, structure, deferred); + Structure* transition = create(vm, structure); transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get()); + transition->setPreviousID(vm, transition, structure); transition->m_nameInPrevious = propertyName.uid(); - transition->setAttributesInPrevious(attributes); - transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); + transition->m_attributesInPrevious = attributes; + transition->m_specificValueInPrevious.setMayBeNull(vm, transition, specificValue); + transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm, transition)); transition->m_offset = structure->m_offset; - transition->m_inferredTypeTable.setMayBeNull(vm, transition, structure->m_inferredTypeTable.get()); - offset = transition->add(vm, propertyName, attributes); + offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); checkOffset(transition->m_offset, transition->inlineCapacity()); { @@ -484,24 +442,6 @@ Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, Proper Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset) { - // NOTE: There are some good reasons why this goes directly to uncacheable dictionary rather than - // caching the removal. We can fix all of these things, but we must remember to do so, if we ever try - // to optimize this case. - // - // - Cached transitions usually steal the property table, and assume that this is possible because they - // can just rebuild the table by looking at past transitions. That code assumes that the table only - // grew and never shrank. To support removals, we'd have to change the property table materialization - // code to handle deletions. Also, we have logic to get the list of properties on a structure that - // lacks a property table by just looking back through the set of transitions since the last - // structure that had a pinned table. That logic would also have to be changed to handle cached - // removals. - // - // - InferredTypeTable assumes that removal has never happened. This is important since if we could - // remove a property and then re-add it later, then the "absence means top" optimization wouldn't - // work anymore, unless removal also either poisoned type inference (by doing something equivalent to - // hasBeenDictionary) or by strongly marking the entry as Top by ensuring that it is not absent, but - // instead, has a null entry. - ASSERT(!structure->isUncacheableDictionary()); Structure* transition = toUncacheableDictionaryTransition(vm, structure); @@ -520,7 +460,7 @@ Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JS DeferGC deferGC(vm.heap); structure->materializePropertyMapIfNecessary(vm, deferGC); - transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); transition->m_offset = structure->m_offset; transition->pin(); @@ -528,6 +468,30 @@ Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JS return transition; } +Structure* Structure::despecifyFunctionTransition(VM& vm, Structure* structure, PropertyName replaceFunction) +{ + ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); + Structure* transition = create(vm, structure); + + ++transition->m_specificFunctionThrashCount; + + DeferGC deferGC(vm.heap); + structure->materializePropertyMapIfNecessary(vm, deferGC); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); + transition->m_offset = structure->m_offset; + transition->pin(); + + if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + transition->despecifyAllFunctions(vm); + else { + bool removed = transition->despecifyFunction(vm, replaceFunction); + ASSERT_UNUSED(removed, removed); + } + + transition->checkOffsetConsistency(); + return transition; +} + Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes) { DeferGC deferGC(vm.heap); @@ -535,7 +499,7 @@ Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, Pr Structure* transition = create(vm, structure); structure->materializePropertyMapIfNecessary(vm, deferGC); - transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); transition->m_offset = structure->m_offset; transition->pin(); @@ -543,7 +507,7 @@ Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, Pr } ASSERT(structure->propertyTable()); - PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid()); + PropertyMapEntry* entry = structure->propertyTable()->find(propertyName.uid()).first; ASSERT(entry); entry->attributes = attributes; @@ -551,27 +515,26 @@ Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, Pr return structure; } -Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind, DeferredStructureTransitionWatchpointFire* deferred) +Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind) { ASSERT(!structure->isUncacheableDictionary()); - Structure* transition = create(vm, structure, deferred); + Structure* transition = create(vm, structure); DeferGC deferGC(vm.heap); structure->materializePropertyMapIfNecessary(vm, deferGC); - transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); transition->m_offset = structure->m_offset; - transition->setDictionaryKind(kind); + transition->m_dictionaryKind = kind; transition->pin(); - transition->setHasBeenDictionary(true); transition->checkOffsetConsistency(); return transition; } -Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred) +Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure) { - return toDictionaryTransition(vm, structure, CachedDictionaryKind, deferred); + return toDictionaryTransition(vm, structure, CachedDictionaryKind); } Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure) @@ -603,13 +566,13 @@ Structure* Structure::freezeTransition(VM& vm, Structure* structure) PropertyTable::iterator iter = transition->propertyTable()->begin(); PropertyTable::iterator end = transition->propertyTable()->end(); if (iter != end) - transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); + transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; for (; iter != end; ++iter) iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); } - ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); - ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); + ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm)); + ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm)); transition->checkOffsetConsistency(); return transition; } @@ -623,22 +586,22 @@ Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) DeferGC deferGC(vm.heap); structure->materializePropertyMapIfNecessary(vm, deferGC); - transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); + transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); transition->m_offset = structure->m_offset; - transition->setPreventExtensions(true); + transition->m_preventExtensions = true; transition->pin(); transition->checkOffsetConsistency(); return transition; } -PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm) +PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm, Structure* owner) { DeferGC deferGC(vm.heap); materializePropertyMapIfNecessaryForPinning(vm, deferGC); - if (isPinnedPropertyTable()) - return propertyTable()->copy(vm, propertyTable()->size() + 1); + if (m_isPinnedPropertyTable) + return propertyTable()->copy(vm, owner, propertyTable()->size() + 1); // Hold the lock while stealing the table - so that getConcurrently() on another thread // will either have to bypass this structure, or will get to use the property table @@ -658,29 +621,27 @@ Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPro if (globalObject->isOriginalArrayStructure(structure)) { Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); if (result->indexingTypeIncludingHistory() == indexingType) { - structure->didTransitionFromThisStructure(); + structure->notifyTransitionFromThisStructure(); return result; } } } - Structure* existingTransition; - if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) { - ASSERT(existingTransition->attributesInPrevious() == attributes); + if (Structure* existingTransition = structure->m_transitionTable.get(0, attributes)) { + ASSERT(existingTransition->m_attributesInPrevious == attributes); ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); return existingTransition; } Structure* transition = create(vm, structure); - transition->setAttributesInPrevious(attributes); - transition->m_blob.setIndexingType(indexingType); - transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); + transition->setPreviousID(vm, transition, structure); + transition->m_attributesInPrevious = attributes; + transition->m_indexingType = indexingType; + transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm, transition)); transition->m_offset = structure->m_offset; checkOffset(transition->m_offset, transition->inlineCapacity()); - if (structure->isDictionary()) - transition->pin(); - else { + { ConcurrentJITLocker locker(structure->m_lock); structure->m_transitionTable.add(vm, transition); } @@ -732,8 +693,6 @@ Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object) { checkOffsetConsistency(); ASSERT(isDictionary()); - - size_t beforeOutOfLineCapacity = this->outOfLineCapacity(); if (isUncacheableDictionary()) { ASSERT(propertyTable()); @@ -759,40 +718,35 @@ Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object) checkOffsetConsistency(); } - setDictionaryKind(NoneDictionaryKind); - setHasBeenFlattenedBefore(true); - - size_t afterOutOfLineCapacity = this->outOfLineCapacity(); - - if (beforeOutOfLineCapacity != afterOutOfLineCapacity) { - ASSERT(beforeOutOfLineCapacity > afterOutOfLineCapacity); - // If the object had a Butterfly but after flattening/compacting we no longer have need of it, - // we need to zero it out because the collector depends on the Structure to know the size for copying. - if (object->butterfly() && !afterOutOfLineCapacity && !this->hasIndexingHeader(object)) - object->setStructureAndButterfly(vm, this, 0); - // If the object was down-sized to the point where the base of the Butterfly is no longer within the - // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to - // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down. - else if (object->butterfly()) - object->shiftButterflyAfterFlattening(vm, beforeOutOfLineCapacity, afterOutOfLineCapacity); - } + m_dictionaryKind = NoneDictionaryKind; + + // If the object had a Butterfly but after flattening/compacting we no longer have need of it, + // we need to zero it out because the collector depends on the Structure to know the size for copying. + if (object->butterfly() && !this->outOfLineCapacity() && !this->hasIndexingHeader(object)) + object->setStructureAndButterfly(vm, this, 0); return this; } -PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes) +PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) { + ASSERT(!enumerationCache()); + + if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) + specificValue = 0; + DeferGC deferGC(vm.heap); materializePropertyMapIfNecessaryForPinning(vm, deferGC); pin(); - return add(vm, propertyName, attributes); + return putSpecificValue(vm, propertyName, attributes, specificValue); } PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName) { ASSERT(isUncacheableDictionary()); + ASSERT(!enumerationCache()); DeferGC deferGC(vm.heap); materializePropertyMapIfNecessaryForPinning(vm, deferGC); @@ -804,181 +758,150 @@ PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName p void Structure::pin() { ASSERT(propertyTable()); - setIsPinnedPropertyTable(true); + m_isPinnedPropertyTable = true; clearPreviousID(); - m_nameInPrevious = nullptr; + m_nameInPrevious.clear(); } void Structure::allocateRareData(VM& vm) { - ASSERT(!hasRareData()); + ASSERT(!typeInfo().structureHasRareData()); StructureRareData* rareData = StructureRareData::create(vm, previous()); - WTF::storeStoreFence(); + m_typeInfo = TypeInfo(typeInfo().type(), typeInfo().flags() | StructureHasRareData); m_previousOrRareData.set(vm, this, rareData); - WTF::storeStoreFence(); - setHasRareData(true); - ASSERT(hasRareData()); -} - -WatchpointSet* Structure::ensurePropertyReplacementWatchpointSet(VM& vm, PropertyOffset offset) -{ - ASSERT(!isUncacheableDictionary()); - - // In some places it's convenient to call this with an invalid offset. So, we do the check here. - if (!isValidOffset(offset)) - return nullptr; - - if (!hasRareData()) - allocateRareData(vm); - ConcurrentJITLocker locker(m_lock); - StructureRareData* rareData = this->rareData(); - if (!rareData->m_replacementWatchpointSets) { - rareData->m_replacementWatchpointSets = - std::make_unique<StructureRareData::PropertyWatchpointMap>(); - WTF::storeStoreFence(); - } - auto result = rareData->m_replacementWatchpointSets->add(offset, nullptr); - if (result.isNewEntry) - result.iterator->value = adoptRef(new WatchpointSet(IsWatched)); - return result.iterator->value.get(); } -void Structure::startWatchingPropertyForReplacements(VM& vm, PropertyName propertyName) +void Structure::cloneRareDataFrom(VM& vm, const Structure* other) { - ASSERT(!isUncacheableDictionary()); - - startWatchingPropertyForReplacements(vm, get(vm, propertyName)); -} - -void Structure::didCachePropertyReplacement(VM& vm, PropertyOffset offset) -{ - ensurePropertyReplacementWatchpointSet(vm, offset)->fireAll("Did cache property replacement"); -} - -void Structure::startWatchingInternalProperties(VM& vm) -{ - if (!isUncacheableDictionary()) { - startWatchingPropertyForReplacements(vm, vm.propertyNames->toString); - startWatchingPropertyForReplacements(vm, vm.propertyNames->valueOf); - } - setDidWatchInternalProperties(true); -} - -void Structure::willStoreValueSlow( - VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize, - InferredTypeTable::StoredPropertyAge age) -{ - ASSERT(!isCompilationThread()); - ASSERT(structure()->classInfo() == info()); - ASSERT(!hasBeenDictionary()); - - // Create the inferred type table before doing anything else, so that we don't GC after we have already - // grabbed a pointer into the property map. - InferredTypeTable* table = m_inferredTypeTable.get(); - if (!table) { - table = InferredTypeTable::create(vm); - WTF::storeStoreFence(); - m_inferredTypeTable.set(vm, this, table); - } - - // This only works if we've got a property table. - PropertyTable* propertyTable; - materializePropertyMapIfNecessary(vm, propertyTable); - - // We must be calling this after having created the given property or confirmed that it was present - // already, so we must have a property table now. - ASSERT(propertyTable); - - // ... and the property must be present. - PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); - ASSERT(entry); - - if (shouldOptimize) - entry->hasInferredType = table->willStoreValue(vm, propertyName, value, age); - else { - table->makeTop(vm, propertyName, age); - entry->hasInferredType = false; - } + ASSERT(other->typeInfo().structureHasRareData()); + StructureRareData* newRareData = StructureRareData::clone(vm, other->rareData()); + m_typeInfo = TypeInfo(typeInfo().type(), typeInfo().flags() | StructureHasRareData); + m_previousOrRareData.set(vm, this, newRareData); } #if DUMP_PROPERTYMAP_STATS -PropertyMapHashTableStats* propertyMapHashTableStats = 0; - struct PropertyMapStatisticsExitLogger { - PropertyMapStatisticsExitLogger(); ~PropertyMapStatisticsExitLogger(); }; -DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, ); +static PropertyMapStatisticsExitLogger logger; -PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger() +PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() { - propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr(); + dataLogF("\nJSC::PropertyMap statistics\n\n"); + dataLogF("%d probes\n", numProbes); + dataLogF("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); + dataLogF("%d rehashes\n", numRehashes); + dataLogF("%d removes\n", numRemoves); } -PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() +#endif + +#if !DO_PROPERTYMAP_CONSTENCY_CHECK + +inline void Structure::checkConsistency() { - unsigned finds = propertyMapHashTableStats->numFinds; - unsigned collisions = propertyMapHashTableStats->numCollisions; - dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID()); - dataLogF("%d finds\n", finds); - dataLogF("%d collisions (%.1f%%)\n", collisions, 100.0 * collisions / finds); - dataLogF("%d lookups\n", propertyMapHashTableStats->numLookups.load()); - dataLogF("%d lookup probings\n", propertyMapHashTableStats->numLookupProbing.load()); - dataLogF("%d adds\n", propertyMapHashTableStats->numAdds.load()); - dataLogF("%d removes\n", propertyMapHashTableStats->numRemoves.load()); - dataLogF("%d rehashes\n", propertyMapHashTableStats->numRehashes.load()); - dataLogF("%d reinserts\n", propertyMapHashTableStats->numReinserts.load()); + checkOffsetConsistency(); } #endif -PropertyTable* Structure::copyPropertyTable(VM& vm) +PropertyTable* Structure::copyPropertyTable(VM& vm, Structure* owner) { if (!propertyTable()) return 0; - return PropertyTable::clone(vm, *propertyTable().get()); + return PropertyTable::clone(vm, owner, *propertyTable().get()); } -PropertyTable* Structure::copyPropertyTableForPinning(VM& vm) +PropertyTable* Structure::copyPropertyTableForPinning(VM& vm, Structure* owner) { if (propertyTable()) - return PropertyTable::clone(vm, *propertyTable().get()); + return PropertyTable::clone(vm, owner, *propertyTable().get()); return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); } -PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid, unsigned& attributes) +PropertyOffset Structure::getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue) { - PropertyOffset result = invalidOffset; + Vector<Structure*, 8> structures; + Structure* structure; + PropertyTable* table; - forEachPropertyConcurrently( - [&] (const PropertyMapEntry& candidate) -> bool { - if (candidate.key != uid) - return true; - - result = candidate.offset; - attributes = candidate.attributes; - return false; - }); + findStructuresAndMapForMaterialization(structures, structure, table); + + if (table) { + PropertyMapEntry* entry = table->find(uid).first; + if (entry) { + attributes = entry->attributes; + specificValue = entry->specificValue.get(); + PropertyOffset result = entry->offset; + structure->m_lock.unlock(); + return result; + } + structure->m_lock.unlock(); + } - return result; + for (unsigned i = structures.size(); i--;) { + structure = structures[i]; + if (structure->m_nameInPrevious.get() != uid) + continue; + + attributes = structure->m_attributesInPrevious; + specificValue = structure->m_specificValueInPrevious.get(); + return structure->m_offset; + } + + return invalidOffset; } -Vector<PropertyMapEntry> Structure::getPropertiesConcurrently() +PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes, JSCell*& specificValue) { - Vector<PropertyMapEntry> result; + ASSERT(!isCompilationThread()); + ASSERT(structure()->classInfo() == info()); - forEachPropertyConcurrently( - [&] (const PropertyMapEntry& entry) -> bool { - result.append(entry); - return true; - }); - - return result; + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + if (!propertyTable()) + return invalidOffset; + + PropertyMapEntry* entry = propertyTable()->find(propertyName.uid()).first; + if (!entry) + return invalidOffset; + + attributes = entry->attributes; + specificValue = entry->specificValue.get(); + return entry->offset; +} + +bool Structure::despecifyFunction(VM& vm, PropertyName propertyName) +{ + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + if (!propertyTable()) + return false; + + PropertyMapEntry* entry = propertyTable()->find(propertyName.uid()).first; + if (!entry) + return false; + + ASSERT(entry->specificValue); + entry->specificValue.clear(); + return true; } -PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attributes) +void Structure::despecifyAllFunctions(VM& vm) +{ + DeferGC deferGC(vm.heap); + materializePropertyMapIfNecessary(vm, deferGC); + if (!propertyTable()) + return; + + PropertyTable::iterator end = propertyTable()->end(); + for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) + iter->specificValue.clear(); +} + +PropertyOffset Structure::putSpecificValue(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) { GCSafeConcurrentJITLocker locker(m_lock, vm.heap); @@ -986,16 +909,16 @@ PropertyOffset Structure::add(VM& vm, PropertyName propertyName, unsigned attrib checkConsistency(); if (attributes & DontEnum) - setHasNonEnumerableProperties(true); + m_hasNonEnumerableProperties = true; - auto rep = propertyName.uid(); + StringImpl* rep = propertyName.uid(); if (!propertyTable()) createPropertyMap(locker, vm); PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity); - propertyTable()->add(PropertyMapEntry(rep, newOffset, attributes), m_offset, PropertyTable::PropertyOffsetMayChange); + propertyTable()->add(PropertyMapEntry(vm, this, rep, newOffset, attributes, specificValue), m_offset, PropertyTable::PropertyOffsetMayChange); checkConsistency(); return newOffset; @@ -1007,7 +930,7 @@ PropertyOffset Structure::remove(PropertyName propertyName) checkConsistency(); - auto rep = propertyName.uid(); + StringImpl* rep = propertyName.uid(); if (!propertyTable()) return invalidOffset; @@ -1040,14 +963,12 @@ void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propert if (!propertyTable()) return; - bool knownUnique = propertyNames.canAddKnownUniqueForStructure(); + bool knownUnique = !propertyNames.size(); PropertyTable::iterator end = propertyTable()->end(); for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { - ASSERT(hasNonEnumerableProperties() || !(iter->attributes & DontEnum)); - if (!(iter->attributes & DontEnum) || mode.includeDontEnumProperties()) { - if (iter->key->isSymbol() && !propertyNames.includeSymbolProperties()) - continue; + ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum)); + if (iter->key->isIdentifier() && (!(iter->attributes & DontEnum) || mode == IncludeDontEnumProperties)) { if (knownUnique) propertyNames.addKnownUnique(iter->key); else @@ -1056,43 +977,6 @@ void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propert } } -void StructureFireDetail::dump(PrintStream& out) const -{ - out.print("Structure transition from ", *m_structure); -} - -DeferredStructureTransitionWatchpointFire::DeferredStructureTransitionWatchpointFire() - : m_structure(nullptr) -{ -} - -DeferredStructureTransitionWatchpointFire::~DeferredStructureTransitionWatchpointFire() -{ - if (m_structure) - m_structure->transitionWatchpointSet().fireAll(StructureFireDetail(m_structure)); -} - -void DeferredStructureTransitionWatchpointFire::add(const Structure* structure) -{ - RELEASE_ASSERT(!m_structure); - RELEASE_ASSERT(structure); - m_structure = structure; -} - -void Structure::didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* deferred) const -{ - // If the structure is being watched, and this is the kind of structure that the DFG would - // like to watch, then make sure to note for all future versions of this structure that it's - // unwise to watch it. - if (m_transitionWatchpointSet.isBeingWatched()) - const_cast<Structure*>(this)->setTransitionWatchpointIsLikelyToBeFired(true); - - if (deferred) - deferred->add(this); - else - m_transitionWatchpointSet.fireAll(StructureFireDetail(this)); -} - JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const { return prototypeForLookup(codeBlock->globalObject()); @@ -1102,6 +986,7 @@ void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) { Structure* thisObject = jsCast<Structure*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); JSCell::visitChildren(thisObject, visitor); visitor.append(&thisObject->m_globalObject); @@ -1112,19 +997,19 @@ void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) visitor.append(&thisObject->m_cachedPrototypeChain); } visitor.append(&thisObject->m_previousOrRareData); + visitor.append(&thisObject->m_specificValueInPrevious); - if (thisObject->isPinnedPropertyTable()) { + if (thisObject->m_isPinnedPropertyTable) { ASSERT(thisObject->m_propertyTableUnsafe); visitor.append(&thisObject->m_propertyTableUnsafe); } else if (thisObject->m_propertyTableUnsafe) thisObject->m_propertyTableUnsafe.clear(); - - visitor.append(&thisObject->m_inferredTypeTable); } bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName) { - if (parseIndex(propertyName)) + unsigned i = propertyName.asIndex(); + if (i != PropertyName::NotAnIndex) return anyObjectInChainMayInterceptIndexedAccesses(); for (Structure* current = this; ;) { @@ -1132,10 +1017,11 @@ bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyN if (prototype.isNull()) return false; - current = prototype.asCell()->structure(vm); + current = prototype.asCell()->structure(); unsigned attributes; - PropertyOffset offset = current->get(vm, propertyName, attributes); + JSCell* specificValue; + PropertyOffset offset = current->get(vm, propertyName, attributes, specificValue); if (!JSC::isValidOffset(offset)) continue; @@ -1146,83 +1032,39 @@ bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyN } } -PassRefPtr<StructureShape> Structure::toStructureShape(JSValue value) -{ - RefPtr<StructureShape> baseShape = StructureShape::create(); - RefPtr<StructureShape> curShape = baseShape; - Structure* curStructure = this; - JSValue curValue = value; - while (curStructure) { - curStructure->forEachPropertyConcurrently( - [&] (const PropertyMapEntry& entry) -> bool { - curShape->addProperty(*entry.key); - return true; - }); - - if (JSObject* curObject = curValue.getObject()) - curShape->setConstructorName(JSObject::calculatedClassName(curObject)); - else - curShape->setConstructorName(curStructure->classInfo()->className); - - if (curStructure->isDictionary()) - curShape->enterDictionaryMode(); - - curShape->markAsFinal(); - - if (curStructure->storedPrototypeStructure()) { - RefPtr<StructureShape> newShape = StructureShape::create(); - curShape->setProto(newShape); - curShape = newShape.release(); - curValue = curStructure->storedPrototype(); - } - - curStructure = curStructure->storedPrototypeStructure(); - } - - return baseShape.release(); -} - -bool Structure::canUseForAllocationsOf(Structure* other) -{ - return inlineCapacity() == other->inlineCapacity() - && storedPrototype() == other->storedPrototype() - && objectInitializationBlob() == other->objectInitializationBlob(); -} - void Structure::dump(PrintStream& out) const { out.print(RawPointer(this), ":[", classInfo()->className, ", {"); + Vector<Structure*, 8> structures; + Structure* structure; + PropertyTable* table; + + const_cast<Structure*>(this)->findStructuresAndMapForMaterialization( + structures, structure, table); + CommaPrinter comma; - const_cast<Structure*>(this)->forEachPropertyConcurrently( - [&] (const PropertyMapEntry& entry) -> bool { - out.print(comma, entry.key, ":", static_cast<int>(entry.offset)); - return true; - }); + if (table) { + PropertyTable::iterator iter = table->begin(); + PropertyTable::iterator end = table->end(); + for (; iter != end; ++iter) + out.print(comma, iter->key, ":", static_cast<int>(iter->offset)); + + structure->m_lock.unlock(); + } + + for (unsigned i = structures.size(); i--;) { + Structure* structure = structures[i]; + if (!structure->m_nameInPrevious) + continue; + out.print(comma, structure->m_nameInPrevious.get(), ":", static_cast<int>(structure->m_offset)); + } out.print("}, ", IndexingTypeDump(indexingType())); if (m_prototype.get().isCell()) out.print(", Proto:", RawPointer(m_prototype.get().asCell())); - - switch (dictionaryKind()) { - case NoneDictionaryKind: - if (hasBeenDictionary()) - out.print(", Has been dictionary"); - break; - case CachedDictionaryKind: - out.print(", Dictionary"); - break; - case UncachedDictionaryKind: - out.print(", UncacheableDictionary"); - break; - } - - if (transitionWatchpointSetIsStillValid()) - out.print(", Leaf"); - else if (transitionWatchpointIsLikelyToBeFired()) - out.print(", Shady leaf"); out.print("]"); } @@ -1249,6 +1091,7 @@ void Structure::dumpContextHeader(PrintStream& out) void PropertyTable::checkConsistency() { + checkOffsetConsistency(); ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); ASSERT(m_indexMask); ASSERT(m_indexSize == m_indexMask + 1); @@ -1286,7 +1129,7 @@ void PropertyTable::checkConsistency() if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) continue; ++nonEmptyEntryCount; - unsigned i = IdentifierRepHash::hash(rep); + unsigned i = rep->existingHash(); unsigned k = 0; unsigned entryIndex; while (1) { @@ -1295,7 +1138,7 @@ void PropertyTable::checkConsistency() if (rep == table()[entryIndex - 1].key) break; if (k == 0) - k = 1 | doubleHash(IdentifierRepHash::hash(rep)); + k = 1 | doubleHash(rep->existingHash()); i += k; } ASSERT(entryIndex == c + 1); @@ -1306,12 +1149,10 @@ void PropertyTable::checkConsistency() void Structure::checkConsistency() { - checkOffsetConsistency(); - if (!propertyTable()) return; - if (!hasNonEnumerableProperties()) { + if (!m_hasNonEnumerableProperties) { PropertyTable::iterator end = propertyTable()->end(); for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { ASSERT(!(iter->attributes & DontEnum)); @@ -1321,19 +1162,12 @@ void Structure::checkConsistency() propertyTable()->checkConsistency(); } -#else - -inline void Structure::checkConsistency() -{ - checkOffsetConsistency(); -} - #endif // DO_PROPERTYMAP_CONSTENCY_CHECK -bool ClassInfo::hasStaticSetterOrReadonlyProperties() const +bool ClassInfo::hasStaticSetterOrReadonlyProperties(VM& vm) const { for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { - if (const HashTable* table = ci->staticPropHashTable) { + if (const HashTable* table = ci->propHashTable(vm)) { if (table->hasSetterOrReadonlyProperties) return true; } @@ -1341,55 +1175,4 @@ bool ClassInfo::hasStaticSetterOrReadonlyProperties() const return false; } -void Structure::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator) -{ - ASSERT(!isDictionary()); - if (!hasRareData()) - allocateRareData(vm); - rareData()->setCachedPropertyNameEnumerator(vm, enumerator); -} - -JSPropertyNameEnumerator* Structure::cachedPropertyNameEnumerator() const -{ - if (!hasRareData()) - return nullptr; - return rareData()->cachedPropertyNameEnumerator(); -} - -bool Structure::canCachePropertyNameEnumerator() const -{ - if (isDictionary()) - return false; - - if (hasIndexedProperties(indexingType())) - return false; - - if (typeInfo().overridesGetPropertyNames()) - return false; - - StructureChain* structureChain = m_cachedPrototypeChain.get(); - ASSERT(structureChain); - WriteBarrier<Structure>* structure = structureChain->head(); - while (true) { - if (!structure->get()) - break; - if (structure->get()->typeInfo().overridesGetPropertyNames()) - return false; - structure++; - } - - return true; -} - -bool Structure::canAccessPropertiesQuickly() const -{ - if (hasNonEnumerableProperties()) - return false; - if (hasGetterSetterProperties()) - return false; - if (isUncacheableDictionary()) - return false; - return true; -} - } // namespace JSC |