Diff
Modified: trunk/JSTests/ChangeLog (293209 => 293210)
--- trunk/JSTests/ChangeLog 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/JSTests/ChangeLog 2022-04-22 05:41:35 UTC (rev 293210)
@@ -1,3 +1,17 @@
+2022-04-21 Yusuke Suzuki <ysuz...@apple.com>
+
+ [JSC] PropertyTable should have compact mode
+ https://bugs.webkit.org/show_bug.cgi?id=239451
+
+ Reviewed by Saam Barati.
+
+ * stress/change-attribute-structure-transition.js:
+ (shouldBe.JSON.stringify.sd):
+ * stress/compact-to-non-compact-with-delete.js: Added.
+ (shouldBe):
+ * stress/compact-to-non-compact.js: Added.
+ (shouldBe):
+
2022-04-14 Caitlin Potter <ca...@igalia.com>
[JSC] ShadowRealm global object has a mutable prototype
Modified: trunk/JSTests/stress/change-attribute-structure-transition.js (293209 => 293210)
--- trunk/JSTests/stress/change-attribute-structure-transition.js 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/JSTests/stress/change-attribute-structure-transition.js 2022-04-22 05:41:35 UTC (rev 293210)
@@ -74,4 +74,4 @@
enumerable: false
});
shouldBe(JSON.stringify(Object.getOwnPropertyDescriptor(object, "test001")), `{"value":42,"writable":false,"enumerable":false,"configurable":true}`);
-shouldBe(JSON.stringify(sd(object)), `[{"offset":-1,"max":1098,"property":null,"type":"unknown"},{"offset":0,"max":1098,"property":"test001","type":"attribute"}]`);
+shouldBe(JSON.stringify(sd(object)), `[{"offset":-1,"max":1062,"property":null,"type":"unknown"},{"offset":0,"max":1062,"property":"test001","type":"attribute"}]`);
Added: trunk/JSTests/stress/compact-to-non-compact-with-delete.js (0 => 293210)
--- trunk/JSTests/stress/compact-to-non-compact-with-delete.js (rev 0)
+++ trunk/JSTests/stress/compact-to-non-compact-with-delete.js 2022-04-22 05:41:35 UTC (rev 293210)
@@ -0,0 +1,23 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var object = {};
+var properties = [];
+
+// compact
+for (var i = 0; i < 100; ++i) {
+ var key = "Hey" + i;
+ object[key] = i;
+}
+for (var i = 0; i < 16; ++i) {
+ var key = "Hey" + i;
+ delete object[key];
+}
+// compact -> non-compact
+for (var i = 0; i < 150; ++i) {
+ var key = "Hey" + (128 + i);
+ object[key] = i;
+}
+shouldBe(JSON.stringify(Object.keys(object)), `["Hey16","Hey17","Hey18","Hey19","Hey20","Hey21","Hey22","Hey23","Hey24","Hey25","Hey26","Hey27","Hey28","Hey29","Hey30","Hey31","Hey32","Hey33","Hey34","Hey35","Hey36","Hey37","Hey38","Hey39","Hey40","Hey41","Hey42","Hey43","Hey44","Hey45","Hey46","Hey47","Hey48","Hey49","Hey50","Hey51","Hey52","Hey53","Hey54","Hey55","Hey56","Hey57","Hey58","Hey59","Hey60","Hey61","Hey62","Hey63","Hey64","Hey65","Hey66","Hey67","Hey68&q
uot;,"Hey69","Hey70","Hey71","Hey72","Hey73","Hey74","Hey75","Hey76","Hey77","Hey78","Hey79","Hey80","Hey81","Hey82","Hey83","Hey84","Hey85","Hey86","Hey87","Hey88","Hey89","Hey90","Hey91","Hey92","Hey93","Hey94","Hey95","Hey96","Hey97","Hey98","Hey99","Hey128","Hey129","Hey130","Hey131","Hey132","Hey133","Hey134","Hey135","Hey136","Hey137","Hey138","Hey139","Hey140","Hey141","Hey142","Hey143","Hey144","Hey145","Hey146","Hey147","Hey148","Hey149","Hey150&quo
t;,"Hey151","Hey152","Hey153","Hey154","Hey155","Hey156","Hey157","Hey158","Hey159","Hey160","Hey161","Hey162","Hey163","Hey164","Hey165","Hey166","Hey167","Hey168","Hey169","Hey170","Hey171","Hey172","Hey173","Hey174","Hey175","Hey176","Hey177","Hey178","Hey179","Hey180","Hey181","Hey182","Hey183","Hey184","Hey185","Hey186","Hey187","Hey188","Hey189","Hey190","Hey191","Hey192","Hey193","Hey194","Hey195","Hey196","Hey197","Hey198","Hey199","Hey200","Hey201","Hey202","
Hey203","Hey204","Hey205","Hey206","Hey207","Hey208","Hey209","Hey210","Hey211","Hey212","Hey213","Hey214","Hey215","Hey216","Hey217","Hey218","Hey219","Hey220","Hey221","Hey222","Hey223","Hey224","Hey225","Hey226","Hey227","Hey228","Hey229","Hey230","Hey231","Hey232","Hey233","Hey234","Hey235","Hey236","Hey237","Hey238","Hey239","Hey240","Hey241","Hey242","Hey243","Hey244","Hey245","Hey246","Hey247","Hey248","Hey249","Hey250","Hey251","Hey252","Hey253","Hey254","Hey255&qu
ot;,"Hey256","Hey257","Hey258","Hey259","Hey260","Hey261","Hey262","Hey263","Hey264","Hey265","Hey266","Hey267","Hey268","Hey269","Hey270","Hey271","Hey272","Hey273","Hey274","Hey275","Hey276","Hey277"]`);
Added: trunk/JSTests/stress/compact-to-non-compact.js (0 => 293210)
--- trunk/JSTests/stress/compact-to-non-compact.js (rev 0)
+++ trunk/JSTests/stress/compact-to-non-compact.js 2022-04-22 05:41:35 UTC (rev 293210)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var object = {};
+var properties = [];
+
+for (var i = 0; i < 128; ++i) {
+ var key = "Hey" + i;
+ properties.push(key);
+ object[key] = i;
+}
+object["MakeNonCompact"] = true;
+properties.push("MakeNonCompact");
+shouldBe(JSON.stringify(Object.keys(object)), JSON.stringify(properties));
+for (var i = 0; i < 128; ++i) {
+ var key = "Hey" + i;
+ shouldBe(object[key], i);
+}
+shouldBe(object["MakeNonCompact"], true);
Modified: trunk/Source/_javascript_Core/ChangeLog (293209 => 293210)
--- trunk/Source/_javascript_Core/ChangeLog 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/ChangeLog 2022-04-22 05:41:35 UTC (rev 293210)
@@ -1,5 +1,146 @@
2022-04-21 Yusuke Suzuki <ysuz...@apple.com>
+ [JSC] PropertyTable should have compact mode
+ https://bugs.webkit.org/show_bug.cgi?id=239451
+
+ Reviewed by Saam Barati.
+
+ This patch introduces "compact" version of PropertyTable. PropertyTable needs to hold two kind of integers: index and property offset.
+ But for most of objects, both are pretty small and they can fit in uint8_t. If we can use uint8_t for both, we can significantly reduce
+ size of allocated memory for PropertyTable (only 40% of memory is required!). This is good for memory, but also good for performance.
+ Now each CompactPropertyTableEntry is 8bytes while original PropertyMapEntry was 16bytes, so CompactPropertyTableEntry can fit in CPU cache well.
+ Also, not allocating large amount of memory can reduce memory allocation / deallocation cost. One of costly destruction of GC-managed objects is
+ PropertyTable (and CodeBlock), and we can reduce that cost by not allocating much memory.
+
+ The approach is following.
+
+ 1. For index vector, we use uint8_t if index can fit within uint8_t.
+ 2. For proprety offset, we use uint8_t and CompactPropertyTableEntry if it is suitable.
+ 3. Once the table gets non-compact, we keep it non-compact since we could have deleted index which has larger than uint8_t. We could improve this
+ strategy when deleted indexes are cleared, but for now, we are taking simple approach.
+ 4. We store isCompactFlag 1 bit in the pointer to the table.
+ 5. We encapsulate functions modifying property table entry in PropertyTable itself, so we do not leak internal compact / non-compact mode to the user
+ of PropertyTable. We remove begin() / end() iterators and instead use forEachproperty, which can implement iteration for each mode more efficiently.
+
+ We have a further opportunity to improve this further: we can deploy 75% load factor only for compact table. Then we can increase threshold of
+ compact table further and keep more and more tables compact mode. Plus, for small sized tables, small backing memory is better in terms of
+ CPU cache hit (and that's measured in WTF::HashTable, and that's why WTF::Hashtable deploys 75% load factor only for small tables). This is left
+ for the subsequent change.
+
+ This change is neutral in JetStream2, 0.3% improvement in Speedometer2 with 80% confidence, and 0.41% improvement in RAMification with 95% confidence.
+
+ * bytecode/ObjectAllocationProfileInlines.h:
+ (JSC::ObjectAllocationProfileBase<Derived>::initializeProfile):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGOperations.cpp:
+ (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
+ * dfg/DFGSpeculativeJIT.cpp:
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+ * ftl/FTLOperations.cpp:
+ (JSC::FTL::JSC_DEFINE_JIT_OPERATION):
+ * runtime/ClonedArguments.h:
+ * runtime/IteratorOperations.cpp:
+ (JSC::createIteratorResultObjectStructure):
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ * runtime/JSGlobalObjectFunctions.cpp:
+ (JSC::JSC_DEFINE_HOST_FUNCTION):
+ * runtime/JSONObject.cpp:
+ (JSC::Stringifier::Holder::appendNextProperty):
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::analyzeHeap):
+ * runtime/JSObject.h:
+ * runtime/ObjectConstructor.h:
+ (JSC::constructEmptyObject):
+ (JSC::createDataPropertyDescriptorObjectStructure):
+ (JSC::createAccessorPropertyDescriptorObjectStructure):
+ * runtime/ObjectConstructorInlines.h:
+ (JSC::objectAssignFast):
+ * runtime/PropertyOffset.h:
+ * runtime/PropertySlot.h:
+ * runtime/PropertyTable.cpp:
+ (JSC::PropertyTable::PropertyTable):
+ (JSC::PropertyTable::finishCreation):
+ (JSC::PropertyTable::visitChildrenImpl):
+ (JSC::PropertyTable::~PropertyTable):
+ (JSC::PropertyTable::seal):
+ (JSC::PropertyTable::freeze):
+ (JSC::PropertyTable::isSealed const):
+ (JSC::PropertyTable::isFrozen const):
+ (JSC::PropertyTable::renumberPropertyOffsets):
+ (JSC::PropertyTable::forEachPropertyMutable):
+ * runtime/PropertyTable.h:
+ (JSC::isPowerOf2):
+ (JSC::nextPowerOf2):
+ (JSC::PropertyTable::findImpl):
+ (JSC::PropertyTable::find):
+ (JSC::PropertyTable::get):
+ (JSC::PropertyTable::add):
+ (JSC::PropertyTable::remove):
+ (JSC::PropertyTable::take):
+ (JSC::PropertyTable::updateAttributeIfExists):
+ (JSC::PropertyTable::sizeInMemory):
+ (JSC::PropertyTable::reinsert):
+ (JSC::PropertyTable::rehash):
+ (JSC::PropertyTable::skipDeletedEntries):
+ (JSC::PropertyTable::usedCount const):
+ (JSC::PropertyTable::dataSize):
+ (JSC::PropertyTable::allocateIndexVector):
+ (JSC::PropertyTable::allocateZeroedIndexVector):
+ (JSC::PropertyTable::destroyIndexVector):
+ (JSC::PropertyTable::canInsert):
+ (JSC::PropertyTable::forEachProperty const):
+ (JSC::PropertyTable::begin): Deleted.
+ (JSC::PropertyTable::end): Deleted.
+ (JSC::PropertyTable::begin const): Deleted.
+ (JSC::PropertyTable::end const): Deleted.
+ (JSC::PropertyTable::table): Deleted.
+ (JSC::PropertyTable::table const): Deleted.
+ * runtime/RegExpMatchesArray.h:
+ * runtime/Structure.cpp:
+ (JSC::Structure::dumpStatistics):
+ (JSC::Structure::Structure):
+ (JSC::Structure::materializePropertyTable):
+ (JSC::Structure::nonPropertyTransitionSlow):
+ (JSC::Structure::isSealed):
+ (JSC::Structure::isFrozen):
+ (JSC::Structure::flattenDictionaryStructure):
+ (JSC::PropertyTableStatisticsExitLogger::~PropertyTableStatisticsExitLogger):
+ (JSC::Structure::getConcurrently):
+ (JSC::Structure::getPropertiesConcurrently):
+ (JSC::Structure::getPropertyNamesFromStructure):
+ (JSC::Structure::toStructureShape):
+ (JSC::Structure::dump const):
+ * runtime/Structure.h:
+ (JSC::CompactPropertyTableEntry::CompactPropertyTableEntry):
+ (JSC::CompactPropertyTableEntry::key const):
+ (JSC::CompactPropertyTableEntry::setKey):
+ (JSC::CompactPropertyTableEntry::offset const):
+ (JSC::CompactPropertyTableEntry::setOffset):
+ (JSC::CompactPropertyTableEntry::attributes const):
+ (JSC::CompactPropertyTableEntry::setAttributes):
+ (JSC::PropertyTableEntry::PropertyTableEntry):
+ (JSC::PropertyTableEntry::key const):
+ (JSC::PropertyTableEntry::setKey):
+ (JSC::PropertyTableEntry::offset const):
+ (JSC::PropertyTableEntry::setOffset):
+ (JSC::PropertyTableEntry::attributes const):
+ (JSC::PropertyTableEntry::setAttributes):
+ (JSC::PropertyMapEntry::PropertyMapEntry): Deleted.
+ * runtime/StructureInlines.h:
+ (JSC::Structure::get):
+ (JSC::Structure::forEachPropertyConcurrently):
+ (JSC::Structure::forEachProperty):
+ (JSC::Structure::add):
+ (JSC::Structure::remove):
+ (JSC::Structure::attributeChange):
+
+2022-04-21 Yusuke Suzuki <ysuz...@apple.com>
+
[JSC] Remove TempRegisterSet
https://bugs.webkit.org/show_bug.cgi?id=239578
Modified: trunk/Source/_javascript_Core/bytecode/ObjectAllocationProfileInlines.h (293209 => 293210)
--- trunk/Source/_javascript_Core/bytecode/ObjectAllocationProfileInlines.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/bytecode/ObjectAllocationProfileInlines.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -67,7 +67,7 @@
}
unsigned inlineCapacity = 0;
- if (inferredInlineCapacity < JSFinalObject::defaultInlineCapacity()) {
+ if (inferredInlineCapacity < JSFinalObject::defaultInlineCapacity) {
// Try to shrink the object based on static analysis.
inferredInlineCapacity += possibleDefaultPropertyCount(vm, prototype);
@@ -74,29 +74,29 @@
if (!inferredInlineCapacity) {
// Empty objects are rare, so most likely the static analyzer just didn't
// see the real initializer function. This can happen with helper functions.
- inferredInlineCapacity = JSFinalObject::defaultInlineCapacity();
- } else if (inferredInlineCapacity > JSFinalObject::defaultInlineCapacity()) {
+ inferredInlineCapacity = JSFinalObject::defaultInlineCapacity;
+ } else if (inferredInlineCapacity > JSFinalObject::defaultInlineCapacity) {
// Default properties are weak guesses, so don't allow them to turn a small
// object into a large object.
- inferredInlineCapacity = JSFinalObject::defaultInlineCapacity();
+ inferredInlineCapacity = JSFinalObject::defaultInlineCapacity;
}
inlineCapacity = inferredInlineCapacity;
- ASSERT(inlineCapacity < JSFinalObject::maxInlineCapacity());
+ ASSERT(inlineCapacity < JSFinalObject::maxInlineCapacity);
} else {
// Normal or large object.
inlineCapacity = inferredInlineCapacity;
- if (inlineCapacity > JSFinalObject::maxInlineCapacity())
- inlineCapacity = JSFinalObject::maxInlineCapacity();
+ if (inlineCapacity > JSFinalObject::maxInlineCapacity)
+ inlineCapacity = JSFinalObject::maxInlineCapacity;
}
if (isPolyProto) {
++inlineCapacity;
- inlineCapacity = std::min(inlineCapacity, JSFinalObject::maxInlineCapacity());
+ inlineCapacity = std::min(inlineCapacity, JSFinalObject::maxInlineCapacity);
}
ASSERT(inlineCapacity > 0);
- ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity());
+ ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity);
size_t allocationSize = JSFinalObject::allocationSize(inlineCapacity);
Allocator allocator = subspaceFor<JSFinalObject>(vm)->allocatorFor(allocationSize, AllocatorForMode::EnsureAllocator);
@@ -105,8 +105,8 @@
if (allocator) {
size_t slop = (allocator.cellSize() - allocationSize) / sizeof(WriteBarrier<Unknown>);
inlineCapacity += slop;
- if (inlineCapacity > JSFinalObject::maxInlineCapacity())
- inlineCapacity = JSFinalObject::maxInlineCapacity();
+ if (inlineCapacity > JSFinalObject::maxInlineCapacity)
+ inlineCapacity = JSFinalObject::maxInlineCapacity;
}
Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype(globalObject, prototype, inlineCapacity, isPolyProto, executable);
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (293209 => 293210)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -3141,7 +3141,7 @@
WTF::loadLoadFence();
if (!isHavingABadTime)
m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
- structure = globalObject->structureCache().emptyObjectStructureConcurrently(base.getObject(), JSFinalObject::defaultInlineCapacity());
+ structure = globalObject->structureCache().emptyObjectStructureConcurrently(base.getObject(), JSFinalObject::defaultInlineCapacity);
}
if (structure) {
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -845,7 +845,7 @@
WTF::loadLoadFence();
if (!isHavingABadTime)
m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
- structure = globalObject->structureCache().emptyObjectStructureConcurrently(base.getObject(), JSFinalObject::defaultInlineCapacity());
+ structure = globalObject->structureCache().emptyObjectStructureConcurrently(base.getObject(), JSFinalObject::defaultInlineCapacity);
}
if (structure) {
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -340,16 +340,16 @@
// Do not clear since Vector::clear shrinks the backing store.
properties.resize(0);
values.clear();
- source->structure()->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
- if (entry.attributes & PropertyAttribute::DontEnum)
+ source->structure()->forEachProperty(vm, [&] (const PropertyTableEntry& entry) -> bool {
+ if (entry.attributes() & PropertyAttribute::DontEnum)
return true;
- PropertyName propertyName(entry.key);
+ PropertyName propertyName(entry.key());
if (propertyName.isPrivateName())
return true;
- properties.append(entry.key);
- values.appendWithCrashOnOverflow(source->getDirect(entry.offset));
+ properties.append(entry.key());
+ values.appendWithCrashOnOverflow(source->getDirect(entry.offset()));
return true;
});
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -13479,15 +13479,15 @@
case NamedPropertyPLoc: {
StringImpl* uid = m_graph.identifiers()[descriptor.info()];
- for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
- if (uid != entry.key)
+ for (const PropertyTableEntry& entry : structure->getPropertiesConcurrently()) {
+ if (uid != entry.key())
continue;
JSValueOperand value(this, edge);
- GPRReg baseGPR = isInlineOffset(entry.offset) ? resultGPR : storageGPR;
+ GPRReg baseGPR = isInlineOffset(entry.offset()) ? resultGPR : storageGPR;
m_jit.storeValue(
value.jsValueRegs(),
- JITCompiler::Address(baseGPR, offsetRelativeToBase(entry.offset)));
+ JITCompiler::Address(baseGPR, offsetRelativeToBase(entry.offset())));
}
break;
}
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -14119,21 +14119,21 @@
}
BitVector setInlineOffsets;
- for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
+ for (const PropertyTableEntry& entry : structure->getPropertiesConcurrently()) {
for (unsigned i = data.m_properties.size(); i--;) {
PromotedLocationDescriptor descriptor = data.m_properties[i];
if (descriptor.kind() != NamedPropertyPLoc)
continue;
- if (m_graph.identifiers()[descriptor.info()] != entry.key)
+ if (m_graph.identifiers()[descriptor.info()] != entry.key())
continue;
LValue base;
- if (isInlineOffset(entry.offset)) {
- setInlineOffsets.set(entry.offset);
+ if (isInlineOffset(entry.offset())) {
+ setInlineOffsets.set(entry.offset());
base = object;
} else
base = butterfly;
- storeProperty(values[i], base, descriptor.info(), entry.offset);
+ storeProperty(values[i], base, descriptor.info(), entry.offset());
break;
}
}
Modified: trunk/Source/_javascript_Core/ftl/FTLOperations.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/ftl/FTLOperations.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/ftl/FTLOperations.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -80,15 +80,15 @@
// minimal visible effects on the system. Also, don't mind
// that this is O(n^2). It doesn't matter. We only get here
// from OSR exit.
- for (PropertyMapEntry entry : structure->getPropertiesConcurrently()) {
+ for (const PropertyTableEntry& entry : structure->getPropertiesConcurrently()) {
for (unsigned i = materialization->properties().size(); i--;) {
const ExitPropertyValue& property = materialization->properties()[i];
if (property.location().kind() != NamedPropertyPLoc)
continue;
- if (codeBlock->identifier(property.location().info()).impl() != entry.key)
+ if (codeBlock->identifier(property.location().info()).impl() != entry.key())
continue;
- object->putDirect(vm, entry.offset, JSValue::decode(values[i]));
+ object->putDirect(vm, entry.offset(), JSValue::decode(values[i]));
}
}
break;
@@ -224,8 +224,8 @@
// field is, for any reason, not filled later.
// We use a random-ish number instead of a sensible value like
// undefined to make possible bugs easier to track.
- for (PropertyMapEntry entry : structure->getPropertiesConcurrently())
- result->putDirect(vm, entry.offset, jsNumber(19723));
+ for (const PropertyTableEntry& entry : structure->getPropertiesConcurrently())
+ result->putDirect(vm, entry.offset(), jsNumber(19723));
return result;
}
Modified: trunk/Source/_javascript_Core/runtime/ClonedArguments.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/ClonedArguments.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/ClonedArguments.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -82,6 +82,6 @@
WriteBarrier<JSFunction> m_callee; // Set to nullptr when we materialize all of our special properties.
};
-static const PropertyOffset clonedArgumentsLengthPropertyOffset = 100;
+static constexpr PropertyOffset clonedArgumentsLengthPropertyOffset = firstOutOfLineOffset;
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/IteratorOperations.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/IteratorOperations.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/IteratorOperations.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -132,7 +132,7 @@
Structure* createIteratorResultObjectStructure(VM& vm, JSGlobalObject& globalObject)
{
- Structure* iteratorResultStructure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity());
+ Structure* iteratorResultStructure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity);
PropertyOffset offset;
iteratorResultStructure = Structure::addPropertyTransition(vm, iteratorResultStructure, vm.propertyNames->value, 0, offset);
RELEASE_ASSERT(offset == valuePropertyOffset);
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -827,7 +827,7 @@
JSFunction::create(vm, this, 0, makeString("set ", vm.propertyNames->underscoreProto.string()), globalFuncProtoSetter));
m_objectPrototype->putDirectNonIndexAccessorWithoutTransition(vm, vm.propertyNames->underscoreProto, protoAccessor, PropertyAttribute::Accessor | PropertyAttribute::DontEnum);
m_functionPrototype->structure()->setPrototypeWithoutTransition(vm, m_objectPrototype.get());
- m_objectStructureForObjectConstructor.set(vm, this, m_structureCache.emptyObjectStructureForPrototype(this, m_objectPrototype.get(), JSFinalObject::defaultInlineCapacity()));
+ m_objectStructureForObjectConstructor.set(vm, this, m_structureCache.emptyObjectStructureForPrototype(this, m_objectPrototype.get(), JSFinalObject::defaultInlineCapacity));
m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf)));
m_speciesGetterSetter.set(vm, this, GetterSetter::create(vm, this, JSFunction::create(vm, globalOperationsSpeciesGetterCodeGenerator(vm), this), nullptr));
@@ -889,7 +889,7 @@
init.set(JSWithScope::createStructure(init.vm, init.owner, jsNull()));
});
- m_nullPrototypeObjectStructure.set(vm, this, JSFinalObject::createStructure(vm, this, jsNull(), JSFinalObject::defaultInlineCapacity()));
+ m_nullPrototypeObjectStructure.set(vm, this, JSFinalObject::createStructure(vm, this, jsNull(), JSFinalObject::defaultInlineCapacity));
m_callbackFunctionStructure.initLater(
[] (const Initializer<Structure>& init) {
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -925,19 +925,19 @@
// that ends up transitioning the structure underneath us.
// https://bugs.webkit.org/show_bug.cgi?id=187837
- source->structure()->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
- PropertyName propertyName(entry.key);
+ source->structure()->forEachProperty(vm, [&] (const PropertyTableEntry& entry) -> bool {
+ PropertyName propertyName(entry.key());
if (propertyName.isPrivateName())
return true;
- if (entry.attributes & PropertyAttribute::DontEnum)
+ if (entry.attributes() & PropertyAttribute::DontEnum)
return true;
if (isPropertyNameExcluded(propertyName))
return true;
- properties.append(entry.key);
- values.appendWithCrashOnOverflow(source->getDirect(entry.offset));
+ properties.append(entry.key());
+ values.appendWithCrashOnOverflow(source->getDirect(entry.offset()));
return true;
});
Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -511,14 +511,14 @@
m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
m_size = m_propertyNames->propertyNameVector().size();
} else if (m_structure && m_object->structureID() == m_structure->id() && canPerformFastPropertyEnumerationForJSONStringify(m_structure)) {
- m_structure->forEachProperty(vm, [&](const PropertyMapEntry& entry) -> bool {
- if (entry.attributes & PropertyAttribute::DontEnum)
+ m_structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool {
+ if (entry.attributes() & PropertyAttribute::DontEnum)
return true;
- PropertyName propertyName(entry.key);
+ PropertyName propertyName(entry.key());
if (propertyName.isSymbol())
return true;
- m_propertiesAndOffsets.constructAndAppend(Identifier::fromUid(vm, entry.key), entry.offset);
+ m_propertiesAndOffsets.constructAndAppend(Identifier::fromUid(vm, entry.key()), entry.offset());
return true;
});
m_hasFastObjectProperties = true;
Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/JSObject.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -430,10 +430,10 @@
Base::analyzeHeap(cell, analyzer);
Structure* structure = thisObject->structure();
- for (auto& entry : structure->getPropertiesConcurrently()) {
- JSValue toValue = thisObject->getDirect(entry.offset);
+ for (const auto& entry : structure->getPropertiesConcurrently()) {
+ JSValue toValue = thisObject->getDirect(entry.offset());
if (toValue && toValue.isCell())
- analyzer.analyzePropertyNameEdge(thisObject, toValue.asCell(), entry.key);
+ analyzer.analyzePropertyNameEdge(thisObject, toValue.asCell(), entry.key());
}
Butterfly* butterfly = thisObject->butterfly();
Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/JSObject.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -1241,17 +1241,13 @@
static inline const TypeInfo typeInfo() { return TypeInfo(FinalObjectType, StructureFlags); }
static constexpr IndexingType defaultIndexingType = NonArray;
- static constexpr unsigned defaultSize = 64;
- static inline unsigned defaultInlineCapacity()
- {
- return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>);
- }
+ static constexpr unsigned defaultSizeInBytes = 64;
+ static constexpr unsigned defaultInlineCapacity = (defaultSizeInBytes - sizeof(JSObject)) / sizeof(WriteBarrier<Unknown>);
+ static_assert(defaultInlineCapacity < firstOutOfLineOffset);
- static constexpr unsigned maxSize = 512;
- static inline unsigned maxInlineCapacity()
- {
- return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>);
- }
+ static constexpr unsigned maxSizeInBytes = 512;
+ static constexpr unsigned maxInlineCapacity = (maxSizeInBytes - sizeof(JSObject)) / sizeof(WriteBarrier<Unknown>);
+ static_assert(maxInlineCapacity < firstOutOfLineOffset);
static JSFinalObject* create(VM&, Structure*);
static JSFinalObject* createWithButterfly(VM&, Structure*, Butterfly*);
Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructor.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/ObjectConstructor.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructor.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -73,7 +73,7 @@
inline JSFinalObject* constructEmptyObject(JSGlobalObject* globalObject, JSObject* prototype)
{
- return constructEmptyObject(globalObject, prototype, JSFinalObject::defaultInlineCapacity());
+ return constructEmptyObject(globalObject, prototype, JSFinalObject::defaultInlineCapacity);
}
inline JSFinalObject* constructEmptyObject(JSGlobalObject* globalObject)
@@ -102,7 +102,7 @@
inline Structure* createDataPropertyDescriptorObjectStructure(VM& vm, JSGlobalObject& globalObject)
{
- Structure* structure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity());
+ Structure* structure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity);
PropertyOffset offset;
structure = Structure::addPropertyTransition(vm, structure, vm.propertyNames->value, 0, offset);
RELEASE_ASSERT(offset == dataPropertyDescriptorValuePropertyOffset);
@@ -117,7 +117,7 @@
inline Structure* createAccessorPropertyDescriptorObjectStructure(VM& vm, JSGlobalObject& globalObject)
{
- Structure* structure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity());
+ Structure* structure = globalObject.structureCache().emptyObjectStructureForPrototype(&globalObject, globalObject.objectPrototype(), JSFinalObject::defaultInlineCapacity);
PropertyOffset offset;
structure = Structure::addPropertyTransition(vm, structure, vm.propertyNames->get, 0, offset);
RELEASE_ASSERT(offset == accessorPropertyDescriptorGetPropertyOffset);
Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructorInlines.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/ObjectConstructorInlines.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructorInlines.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -76,16 +76,16 @@
// Do not clear since Vector::clear shrinks the backing store.
properties.resize(0);
values.clear();
- source->structure()->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
- if (entry.attributes & PropertyAttribute::DontEnum)
+ source->structure()->forEachProperty(vm, [&] (const PropertyTableEntry& entry) -> bool {
+ if (entry.attributes() & PropertyAttribute::DontEnum)
return true;
- PropertyName propertyName(entry.key);
+ PropertyName propertyName(entry.key());
if (propertyName.isPrivateName())
return true;
- properties.append(entry.key);
- values.appendWithCrashOnOverflow(source->getDirect(entry.offset));
+ properties.append(entry.key());
+ values.appendWithCrashOnOverflow(source->getDirect(entry.offset()));
return true;
});
Modified: trunk/Source/_javascript_Core/runtime/PropertyOffset.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/PropertyOffset.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/PropertyOffset.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -32,7 +32,7 @@
typedef int PropertyOffset;
static constexpr PropertyOffset invalidOffset = -1;
-static constexpr PropertyOffset firstOutOfLineOffset = 100;
+static constexpr PropertyOffset firstOutOfLineOffset = 64;
static constexpr PropertyOffset knownPolyProtoOffset = 0;
static_assert(knownPolyProtoOffset < firstOutOfLineOffset, "We assume in all the JITs that the poly proto offset is an inline offset");
Modified: trunk/Source/_javascript_Core/runtime/PropertySlot.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/PropertySlot.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/PropertySlot.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -49,7 +49,7 @@
AccessorOrCustomAccessorOrValue = Accessor | CustomAccessor | CustomValue,
ReadOnlyOrAccessorOrCustomAccessor = ReadOnly | Accessor | CustomAccessor,
- // Things that are used by static hashtables are not in the attributes byte in PropertyMapEntry.
+ // Things that are used by static hashtables are not in the attributes byte in PropertyTableEntry.
Function = 1 << 8, // property is a function - only used by static hashtables
Builtin = 1 << 9, // property is a builtin function - only used by static hashtables
ConstantInteger = 1 << 10, // property is a constant integer - only used by static hashtables
Modified: trunk/Source/_javascript_Core/runtime/PropertyTable.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/PropertyTable.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/PropertyTable.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -59,11 +59,14 @@
: JSCell(vm, vm.propertyTableStructure.get())
, m_indexSize(sizeForCapacity(initialCapacity))
, m_indexMask(m_indexSize - 1)
- , m_index(static_cast<unsigned*>(PropertyTableMalloc::zeroedMalloc(dataSize())))
+ , m_indexVector()
, m_keyCount(0)
, m_deletedCount(0)
{
ASSERT(isPowerOf2(m_indexSize));
+ bool isCompact = tableCapacity() < UINT8_MAX;
+ m_indexVector = allocateZeroedIndexVector(isCompact, m_indexSize);
+ ASSERT(isCompact == this->isCompact());
}
PropertyTable::PropertyTable(VM& vm, const PropertyTable& other)
@@ -70,18 +73,19 @@
: JSCell(vm, vm.propertyTableStructure.get())
, m_indexSize(other.m_indexSize)
, m_indexMask(other.m_indexMask)
- , m_index(static_cast<unsigned*>(PropertyTableMalloc::malloc(dataSize())))
+ , m_indexVector(allocateIndexVector(other.isCompact(), other.m_indexSize))
, m_keyCount(other.m_keyCount)
, m_deletedCount(other.m_deletedCount)
{
ASSERT(isPowerOf2(m_indexSize));
+ ASSERT(isCompact() == other.isCompact());
+ memcpy(bitwise_cast<void*>(m_indexVector & indexVectorMask), bitwise_cast<void*>(other.m_indexVector & indexVectorMask), dataSize(isCompact()));
- memcpy(m_index, other.m_index, dataSize());
+ forEachProperty([&](auto& entry) {
+ entry.key()->ref();
+ return IterationStatus::Continue;
+ });
- iterator end = this->end();
- for (iterator iter = begin(); iter != end; ++iter)
- iter->key->ref();
-
// Copy the m_deletedOffsets vector.
Vector<PropertyOffset>* otherDeletedOffsets = other.m_deletedOffsets.get();
if (otherDeletedOffsets)
@@ -92,19 +96,25 @@
: JSCell(vm, vm.propertyTableStructure.get())
, m_indexSize(sizeForCapacity(initialCapacity))
, m_indexMask(m_indexSize - 1)
- , m_index(static_cast<unsigned*>(PropertyTableMalloc::zeroedMalloc(dataSize())))
+ , m_indexVector()
, m_keyCount(0)
, m_deletedCount(0)
{
ASSERT(isPowerOf2(m_indexSize));
ASSERT(initialCapacity >= other.m_keyCount);
+ bool isCompact = other.isCompact() && tableCapacity() < UINT8_MAX;
+ m_indexVector = allocateZeroedIndexVector(isCompact, m_indexSize);
+ ASSERT(this->isCompact() == isCompact);
- const_iterator end = other.end();
- for (const_iterator iter = other.begin(); iter != end; ++iter) {
- ASSERT(canInsert());
- reinsert(*iter);
- iter->key->ref();
- }
+ withIndexVector([&](auto* vector) {
+ auto* table = tableFromIndexVector(vector);
+ other.forEachProperty([&](auto& entry) {
+ ASSERT(canInsert(entry));
+ reinsert(vector, table, entry);
+ entry.key()->ref();
+ return IterationStatus::Continue;
+ });
+ });
// Copy the m_deletedOffsets vector.
Vector<PropertyOffset>* otherDeletedOffsets = other.m_deletedOffsets.get();
@@ -115,7 +125,7 @@
void PropertyTable::finishCreation(VM& vm)
{
Base::finishCreation(vm);
- vm.heap.reportExtraMemoryAllocated(dataSize());
+ vm.heap.reportExtraMemoryAllocated(dataSize(isCompact()));
}
template<typename Visitor>
@@ -124,7 +134,7 @@
auto* thisObject = jsCast<PropertyTable*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(cell, visitor);
- visitor.reportExtraMemoryVisited(thisObject->dataSize());
+ visitor.reportExtraMemoryVisited(thisObject->dataSize(thisObject->isCompact()));
}
DEFINE_VISIT_CHILDREN(PropertyTable);
@@ -136,13 +146,92 @@
PropertyTable::~PropertyTable()
{
- iterator end = this->end();
- for (iterator iter = begin(); iter != end; ++iter)
- iter->key->deref();
+ forEachProperty([&](auto& entry) {
+ entry.key()->deref();
+ return IterationStatus::Continue;
+ });
+ destroyIndexVector(m_indexVector);
+}
- PropertyTableMalloc::free(m_index);
+void PropertyTable::seal()
+{
+ forEachPropertyMutable([&](auto& entry) {
+ entry.setAttributes(entry.attributes() | static_cast<unsigned>(PropertyAttribute::DontDelete));
+ return IterationStatus::Continue;
+ });
+}
+void PropertyTable::freeze()
+{
+ forEachPropertyMutable([&](auto& entry) {
+ if (!(entry.attributes() & PropertyAttribute::Accessor))
+ entry.setAttributes(entry.attributes() | static_cast<unsigned>(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
+ else
+ entry.setAttributes(entry.attributes() | static_cast<unsigned>(PropertyAttribute::DontDelete));
+ return IterationStatus::Continue;
+ });
}
+bool PropertyTable::isSealed() const
+{
+ bool result = true;
+ forEachProperty([&](const auto& entry) {
+ if ((entry.attributes() & PropertyAttribute::DontDelete) != static_cast<unsigned>(PropertyAttribute::DontDelete)) {
+ result = false;
+ return IterationStatus::Done;
+ }
+ return IterationStatus::Continue;
+ });
+ return result;
+}
+
+bool PropertyTable::isFrozen() const
+{
+ bool result = true;
+ forEachProperty([&](const auto& entry) {
+ if (!(entry.attributes() & PropertyAttribute::DontDelete)) {
+ result = false;
+ return IterationStatus::Done;
+ }
+ if (!(entry.attributes() & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor))) {
+ result = false;
+ return IterationStatus::Done;
+ }
+ return IterationStatus::Continue;
+ });
+ return result;
+}
+
+PropertyOffset PropertyTable::renumberPropertyOffsets(JSObject* object, unsigned inlineCapacity, Vector<JSValue>& values)
+{
+ ASSERT(values.size() == size());
+ unsigned i = 0;
+ PropertyOffset offset = invalidOffset;
+ forEachPropertyMutable([&](auto& entry) {
+ values[i] = object->getDirect(entry.offset());
+ offset = offsetForPropertyNumber(i, inlineCapacity);
+ entry.setOffset(offset);
+ ++i;
+ return IterationStatus::Continue;
+ });
+ clearDeletedOffsets();
+ return offset;
+}
+
+template<typename Functor>
+inline void PropertyTable::forEachPropertyMutable(const Functor& functor)
+{
+ withIndexVector([&](auto* vector) {
+ auto* cursor = tableFromIndexVector(vector);
+ auto* end = tableEndFromIndexVector(vector);
+ for (; cursor != end; ++cursor) {
+ if (cursor->key() == PROPERTY_MAP_DELETED_ENTRY_KEY)
+ continue;
+ if (functor(*cursor) == IterationStatus::Done)
+ return;
+ }
+ });
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/PropertyTable.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/PropertyTable.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/PropertyTable.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -57,12 +57,12 @@
#endif
-inline bool isPowerOf2(unsigned v)
+inline constexpr bool isPowerOf2(unsigned v)
{
return hasOneBitSet(v);
}
-inline unsigned nextPowerOf2(unsigned v)
+inline constexpr unsigned nextPowerOf2(unsigned v)
{
// Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
// Devised by Sean Anderson, Sepember 14, 2001
@@ -78,52 +78,33 @@
return v;
}
+// compact <-> non-compact PropertyTable
+// We need to maintain two things, one is PropertyOffset and one is unsigned index in index buffer of PropertyTable.
+// But both are typically small. It is possible that we can get optimized table if both are fit in uint8_t, that's
+// compact PropertyTable.
+//
+// PropertyOffset can be offseted with firstOutOfLineOffset since we can get out-of-line property easily, but this
+// offset is small enough (64 currently), so that we can still assume that most of property offsets are < 256.
+//
+// 1. If property offset gets larger than 255, then we get non-compact PropertyTable. It requires at least 191 (255 - 64) properties.
+// In that case, PropertyTable size should be 256 since it is power-of-two.
+// 2. If index gets larger than 255, then we get non-compact PropertyTable. But we are using 0 and 255 for markers. Thus, if we get 253
+// used counts, then we need to change the table.
+//
+// So, typical scenario is that, once 128th property is added, then we extend the table via rehashing. At that time, we change the
+// table from compact to non-compact mode.
+//
+// index-size table-capacity compact v.s. non-compact
+// 16 8 80 192
+// 32 16 160 384
+// 64 32 320 768
+// 128 64 640 1536
+// 256 128 1280 3072
+// 512 256 N/A 6144 // After 512 size, compact PropertyTable does not work. All table gets non-compact.
+
class PropertyTable final : public JSCell {
-
- // This is the implementation for 'iterator' and 'const_iterator',
- // used for iterating over the table in insertion order.
- template<typename T>
- class ordered_iterator {
- public:
- ordered_iterator<T>& operator++()
- {
- m_valuePtr = skipDeletedEntries(m_valuePtr + 1, m_endValuePtr);
- return *this;
- }
-
- bool operator==(const ordered_iterator<T>& other) const
- {
- return m_valuePtr == other.m_valuePtr;
- }
-
- bool operator!=(const ordered_iterator<T>& other) const
- {
- return m_valuePtr != other.m_valuePtr;
- }
-
- T& operator*()
- {
- return *m_valuePtr;
- }
-
- T* operator->()
- {
- return m_valuePtr;
- }
-
- ordered_iterator(T* valuePtr, T* endValuePtr)
- : m_valuePtr(valuePtr)
- , m_endValuePtr(endValuePtr)
- {
- }
-
- private:
- T* m_valuePtr;
- T* m_endValuePtr;
- };
-
public:
- typedef JSCell Base;
+ using Base = JSCell;
static constexpr unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
template<typename CellType, SubspaceAccess>
@@ -143,18 +124,9 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
}
- typedef UniquedStringImpl* KeyType;
- typedef PropertyMapEntry ValueType;
+ using KeyType = UniquedStringImpl*;
+ using ValueType = PropertyTableEntry;
- // The in order iterator provides overloaded * and -> to access the Value at the current position.
- typedef ordered_iterator<ValueType> iterator;
- typedef ordered_iterator<const ValueType> const_iterator;
-
- // The find_iterator is a pair of a pointer to a Value* an the entry in the index.
- // If 'find' does not find an entry then iter.first will be 0, and iter.second will
- // give the point in m_index where an entry should be inserted.
- typedef std::pair<ValueType*, unsigned> find_iterator;
-
// Constructor is passed an initial capacity, a PropertyTable to copy, or both.
static PropertyTable* create(VM&, unsigned initialCapacity);
static PropertyTable* clone(VM&, const PropertyTable&);
@@ -161,21 +133,22 @@
static PropertyTable* clone(VM&, unsigned initialCapacity, const PropertyTable&);
~PropertyTable();
- // Ordered iteration methods.
- iterator begin();
- iterator end();
- const_iterator begin() const;
- const_iterator end() const;
-
// Find a value in the table.
- find_iterator find(const KeyType&);
- ValueType* get(const KeyType&);
+ std::tuple<PropertyOffset, unsigned> get(const KeyType&);
// Add a value to the table
- std::pair<find_iterator, bool> WARN_UNUSED_RETURN add(VM&, const ValueType& entry);
+ std::tuple<PropertyOffset, unsigned, bool> WARN_UNUSED_RETURN add(VM&, const ValueType& entry);
// Remove a value from the table.
- void remove(VM&, const find_iterator&);
- void remove(VM&, const KeyType&);
+ std::tuple<PropertyOffset, unsigned> take(VM&, const KeyType&);
+ PropertyOffset updateAttributeIfExists(const KeyType&, unsigned attributes);
+ PropertyOffset renumberPropertyOffsets(JSObject*, unsigned inlineCapacity, Vector<JSValue>&);
+
+ void seal();
+ void freeze();
+
+ bool isSealed() const;
+ bool isFrozen() const;
+
// Returns the number of values in the hashtable.
unsigned size() const;
@@ -200,11 +173,10 @@
size_t sizeInMemory();
void checkConsistency();
#endif
-
- static ptrdiff_t offsetOfIndexSize() { return OBJECT_OFFSETOF(PropertyTable, m_indexSize); }
- static ptrdiff_t offsetOfIndexMask() { return OBJECT_OFFSETOF(PropertyTable, m_indexMask); }
- static ptrdiff_t offsetOfIndex() { return OBJECT_OFFSETOF(PropertyTable, m_index); }
+ template<typename Functor>
+ void forEachProperty(const Functor&) const;
+
static constexpr unsigned EmptyEntryIndex = 0;
private:
@@ -217,10 +189,13 @@
void finishCreation(VM&);
// Used to insert a value known not to be in the table, and where we know capacity to be available.
- void reinsert(const ValueType& entry);
+ template<typename Index, typename Entry>
+ void reinsert(Index*, Entry*, const ValueType& entry);
+ static bool canFitInCompact(const ValueType& entry) { return entry.offset() <= UINT8_MAX; }
+
// Rehash the table. Used to grow, or to recover deleted slots.
- void rehash(VM&, unsigned newCapacity);
+ void rehash(VM&, unsigned newCapacity, bool canStayCompact);
// The capacity of the table of values is half of the size of the index.
unsigned tableCapacity() const;
@@ -238,63 +213,113 @@
template<typename T>
static T* skipDeletedEntries(T* valuePtr, T* endValuePtr);
- // The table of values lies after the hash index.
- ValueType* table();
- const ValueType* table() const;
-
- ValueType* tableEnd() { return table() + usedCount(); }
- const ValueType* tableEnd() const { return table() + usedCount(); }
-
// total number of used entries in the values array - by either valid entries, or deleted ones.
unsigned usedCount() const;
// The size in bytes of data needed for by the table.
- size_t dataSize();
+ size_t dataSize(bool isCompact);
+ static size_t dataSize(bool isCompact, unsigned indexSize);
// Calculates the appropriate table size (rounds up to a power of two).
static unsigned sizeForCapacity(unsigned capacity);
// Check if capacity is available.
- bool canInsert();
+ bool canInsert(const ValueType&);
+ void remove(VM&, KeyType, unsigned entryIndex, unsigned index);
+
+ struct FindResult {
+ unsigned entryIndex;
+ unsigned index;
+ PropertyOffset offset;
+ unsigned attributes;
+ };
+
+ FindResult find(const KeyType&);
+
+ template<typename Index, typename Entry>
+ ALWAYS_INLINE FindResult findImpl(const Index*, const Entry*, const KeyType&);
+
+ bool isCompact() const { return m_indexVector & isCompactFlag; }
+
+ template<typename Functor>
+ void forEachPropertyMutable(const Functor&);
+
+ // The table of values lies after the hash index.
+ static CompactPropertyTableEntry* tableFromIndexVector(uint8_t* index, unsigned indexSize)
+ {
+ return bitwise_cast<CompactPropertyTableEntry*>(index + indexSize);
+ }
+ static const CompactPropertyTableEntry* tableFromIndexVector(const uint8_t* index, unsigned indexSize)
+ {
+ return bitwise_cast<const CompactPropertyTableEntry*>(index + indexSize);
+ }
+ static PropertyTableEntry* tableFromIndexVector(uint32_t* index, unsigned indexSize)
+ {
+ return bitwise_cast<PropertyTableEntry*>(index + indexSize);
+ }
+ static const PropertyTableEntry* tableFromIndexVector(const uint32_t* index, unsigned indexSize)
+ {
+ return bitwise_cast<const PropertyTableEntry*>(index + indexSize);
+ }
+
+ CompactPropertyTableEntry* tableFromIndexVector(uint8_t* index) { return tableFromIndexVector(index, m_indexSize); }
+ const CompactPropertyTableEntry* tableFromIndexVector(const uint8_t* index) const { return tableFromIndexVector(index, m_indexSize); }
+ PropertyTableEntry* tableFromIndexVector(uint32_t* index) { return tableFromIndexVector(index, m_indexSize); }
+ const PropertyTableEntry* tableFromIndexVector(const uint32_t* index) const { return tableFromIndexVector(index, m_indexSize); }
+
+ CompactPropertyTableEntry* tableEndFromIndexVector(uint8_t* index)
+ {
+ return tableFromIndexVector(index) + usedCount();
+ }
+ const CompactPropertyTableEntry* tableEndFromIndexVector(const uint8_t* index) const
+ {
+ return tableFromIndexVector(index) + usedCount();
+ }
+ PropertyTableEntry* tableEndFromIndexVector(uint32_t* index)
+ {
+ return tableFromIndexVector(index) + usedCount();
+ }
+ const PropertyTableEntry* tableEndFromIndexVector(const uint32_t* index) const
+ {
+ return tableFromIndexVector(index) + usedCount();
+ }
+
+ static uintptr_t allocateIndexVector(bool isCompact, unsigned indexSize);
+ static uintptr_t allocateZeroedIndexVector(bool isCompact, unsigned indexSize);
+ static void destroyIndexVector(uintptr_t indexVector);
+
+ template<typename Func>
+ static ALWAYS_INLINE auto withIndexVector(uintptr_t indexVector, Func&& function) -> decltype(auto)
+ {
+ if (indexVector & isCompactFlag)
+ return function(bitwise_cast<uint8_t*>(indexVector & indexVectorMask));
+ return function(bitwise_cast<uint32_t*>(indexVector & indexVectorMask));
+ }
+
+ template<typename Func>
+ ALWAYS_INLINE auto withIndexVector(Func&& function) const -> decltype(auto)
+ {
+ return withIndexVector(m_indexVector, std::forward<Func>(function));
+ }
+
+ static constexpr uintptr_t isCompactFlag = 0x1;
+ static constexpr uintptr_t indexVectorMask = ~isCompactFlag;
+
unsigned m_indexSize;
unsigned m_indexMask;
- unsigned* m_index;
+ uintptr_t m_indexVector;
unsigned m_keyCount;
unsigned m_deletedCount;
std::unique_ptr<Vector<PropertyOffset>> m_deletedOffsets;
static constexpr unsigned MinimumTableSize = 16;
+ static_assert(MinimumTableSize >= 16, "compact index is uint8_t and we should keep 16 byte aligned entries after this array");
};
-inline PropertyTable::iterator PropertyTable::begin()
+template<typename Index, typename Entry>
+PropertyTable::FindResult PropertyTable::findImpl(const Index* indexVector, const Entry* table, const KeyType& key)
{
- auto* tableEnd = this->tableEnd();
- return iterator(skipDeletedEntries(table(), tableEnd), tableEnd);
-}
-
-inline PropertyTable::iterator PropertyTable::end()
-{
- auto* tableEnd = this->tableEnd();
- return iterator(tableEnd, tableEnd);
-}
-
-inline PropertyTable::const_iterator PropertyTable::begin() const
-{
- auto* tableEnd = this->tableEnd();
- return const_iterator(skipDeletedEntries(table(), tableEnd), tableEnd);
-}
-
-inline PropertyTable::const_iterator PropertyTable::end() const
-{
- auto* tableEnd = this->tableEnd();
- return const_iterator(tableEnd, tableEnd);
-}
-
-inline PropertyTable::find_iterator PropertyTable::find(const KeyType& key)
-{
- ASSERT(key);
- ASSERT(key->isAtom() || key->isSymbol());
unsigned hash = IdentifierRepHash::hash(key);
#if DUMP_PROPERTYMAP_STATS
@@ -302,11 +327,15 @@
#endif
while (true) {
- unsigned entryIndex = m_index[hash & m_indexMask];
+ unsigned index = hash & m_indexMask;
+ unsigned entryIndex = indexVector[index];
if (entryIndex == EmptyEntryIndex)
- return std::make_pair((ValueType*)nullptr, hash & m_indexMask);
- if (key == table()[entryIndex - 1].key)
- return std::make_pair(&table()[entryIndex - 1], hash & m_indexMask);
+ return FindResult { entryIndex, index, invalidOffset, 0 };
+ const auto& entry = table[entryIndex - 1];
+ if (key == entry.key()) {
+ ASSERT(!m_deletedOffsets || !m_deletedOffsets->contains(entry.offset()));
+ return FindResult { entryIndex, index, entry.offset(), entry.attributes() };
+ }
#if DUMP_PROPERTYMAP_STATS
++propertyTableStats->numCollisions;
@@ -314,7 +343,7 @@
#if DUMP_PROPERTYMAP_COLLISIONS
dataLog("PropertyTable collision for ", key, " (", hash, ")\n");
- dataLog("Collided with ", table()[entryIndex - 1].key, "(", IdentifierRepHash::hash(table()[entryIndex - 1].key), ")\n");
+ dataLog("Collided with ", entry.key(), "(", IdentifierRepHash::hash(entry.key()), ")\n");
#endif
hash++;
@@ -321,46 +350,36 @@
}
}
-inline PropertyTable::ValueType* PropertyTable::get(const KeyType& key)
+inline PropertyTable::FindResult PropertyTable::find(const KeyType& key)
{
ASSERT(key);
ASSERT(key->isAtom() || key->isSymbol());
+ return withIndexVector([&](auto* vector) {
+ return findImpl(vector, tableFromIndexVector(vector), key);
+ });
+}
+
+inline std::tuple<PropertyOffset, unsigned> PropertyTable::get(const KeyType& key)
+{
+ ASSERT(key);
+ ASSERT(key->isAtom() || key->isSymbol());
ASSERT(key != PROPERTY_MAP_DELETED_ENTRY_KEY);
if (!m_keyCount)
- return nullptr;
+ return std::tuple { invalidOffset, 0 };
- unsigned hash = IdentifierRepHash::hash(key);
-
-#if DUMP_PROPERTYMAP_STATS
- ++propertyTableStats->numLookups;
-#endif
-
- while (true) {
- unsigned entryIndex = m_index[hash & m_indexMask];
- if (entryIndex == EmptyEntryIndex)
- return nullptr;
- if (key == table()[entryIndex - 1].key) {
- ASSERT(!m_deletedOffsets || !m_deletedOffsets->contains(table()[entryIndex - 1].offset));
- return &table()[entryIndex - 1];
- }
-
-#if DUMP_PROPERTYMAP_STATS
- ++propertyTableStats->numLookupProbing;
-#endif
-
- hash++;
- }
+ FindResult result = find(key);
+ return std::tuple { result.offset, result.attributes };
}
-inline std::pair<PropertyTable::find_iterator, bool> WARN_UNUSED_RETURN PropertyTable::add(VM& vm, const ValueType& entry)
+inline std::tuple<PropertyOffset, unsigned, bool> WARN_UNUSED_RETURN PropertyTable::add(VM& vm, const ValueType& entry)
{
- ASSERT(!m_deletedOffsets || !m_deletedOffsets->contains(entry.offset));
+ ASSERT(!m_deletedOffsets || !m_deletedOffsets->contains(entry.offset()));
// Look for a value with a matching key already in the array.
- find_iterator iter = find(entry.key);
- if (iter.first)
- return std::make_pair(iter, false);
+ FindResult result = find(entry.key());
+ if (result.offset != invalidOffset)
+ return std::tuple { result.offset, result.attributes, false };
#if DUMP_PROPERTYMAP_STATS
++propertyTableStats->numAdds;
@@ -367,32 +386,32 @@
#endif
// Ref the key
- entry.key->ref();
+ entry.key()->ref();
// ensure capacity is available.
- if (!canInsert()) {
- rehash(vm, m_keyCount + 1);
- iter = find(entry.key);
- ASSERT(!iter.first);
+ if (!canInsert(entry)) {
+ rehash(vm, m_keyCount + 1, canFitInCompact(entry));
+ result = find(entry.key());
+ ASSERT(result.offset == invalidOffset);
+ ASSERT(result.entryIndex == EmptyEntryIndex);
}
// Allocate a slot in the hashtable, and set the index to reference this.
+ ASSERT(!isCompact() || usedCount() < UINT8_MAX);
+ unsigned index = result.index;
unsigned entryIndex = usedCount() + 1;
- m_index[iter.second] = entryIndex;
- iter.first = &table()[entryIndex - 1];
- *iter.first = entry;
+ withIndexVector([&](auto* vector) {
+ vector[index] = entryIndex;
+ tableFromIndexVector(vector)[entryIndex - 1] = entry;
+ });
++m_keyCount;
- return std::make_pair(iter, true);
+ return std::tuple { entry.offset(), entry.attributes(), true };
}
-inline void PropertyTable::remove(VM& vm, const find_iterator& iter)
+inline void PropertyTable::remove(VM& vm, KeyType key, unsigned entryIndex, unsigned index)
{
- // Removing a key that doesn't exist does nothing!
- if (!iter.first)
- return;
-
#if DUMP_PROPERTYMAP_STATS
++propertyTableStats->numRemoves;
#endif
@@ -399,9 +418,11 @@
// Replace this one element with the deleted sentinel. Also clear out
// the entry so we can iterate all the entries as needed.
- m_index[iter.second] = deletedEntryIndex();
- iter.first->key->deref();
- iter.first->key = PROPERTY_MAP_DELETED_ENTRY_KEY;
+ withIndexVector([&](auto* vector) {
+ vector[index] = deletedEntryIndex();
+ tableFromIndexVector(vector)[entryIndex - 1].setKey(PROPERTY_MAP_DELETED_ENTRY_KEY);
+ });
+ key->deref();
ASSERT(m_keyCount >= 1);
--m_keyCount;
@@ -408,14 +429,29 @@
++m_deletedCount;
if (m_deletedCount * 4 >= m_indexSize)
- rehash(vm, m_keyCount);
+ rehash(vm, m_keyCount, true);
}
-inline void PropertyTable::remove(VM& vm, const KeyType& key)
+inline std::tuple<PropertyOffset, unsigned> PropertyTable::take(VM& vm, const KeyType& key)
{
- remove(vm, find(key));
+ FindResult result = find(key);
+ if (result.offset != invalidOffset)
+ remove(vm, key, result.entryIndex, result.index);
+ return std::tuple { result.offset, result.attributes };
}
+inline PropertyOffset PropertyTable::updateAttributeIfExists(const KeyType& key, unsigned attributes)
+{
+ return withIndexVector([&](auto* vector) -> PropertyOffset {
+ auto* table = tableFromIndexVector(vector);
+ FindResult result = findImpl(vector, table, key);
+ if (result.offset == invalidOffset)
+ return invalidOffset;
+ table[result.entryIndex - 1].setAttributes(attributes);
+ return result.offset;
+ });
+}
+
// returns the number of values in the hashtable.
inline unsigned PropertyTable::size() const
{
@@ -479,7 +515,7 @@
#ifndef NDEBUG
inline size_t PropertyTable::sizeInMemory()
{
- size_t result = sizeof(PropertyTable) + dataSize();
+ size_t result = sizeof(PropertyTable) + dataSize(isCompact());
if (m_deletedOffsets)
result += (m_deletedOffsets->capacity() * sizeof(PropertyOffset));
return result;
@@ -486,7 +522,8 @@
}
#endif
-inline void PropertyTable::reinsert(const ValueType& entry)
+template<typename Index, typename Entry>
+inline void PropertyTable::reinsert(Index* indexVector, Entry* table, const ValueType& entry)
{
#if DUMP_PROPERTYMAP_STATS
++propertyTableStats->numReinserts;
@@ -494,27 +531,30 @@
// Used to insert a value known not to be in the table, and where
// we know capacity to be available.
- ASSERT(canInsert());
- find_iterator iter = find(entry.key);
- ASSERT(!iter.first);
+ ASSERT(canInsert(entry));
+ FindResult result = findImpl(indexVector, table, entry.key());
+ ASSERT(result.offset == invalidOffset);
+ ASSERT(result.entryIndex == EmptyEntryIndex);
+ ASSERT(!isCompact() || usedCount() < UINT8_MAX);
unsigned entryIndex = usedCount() + 1;
- m_index[iter.second] = entryIndex;
- table()[entryIndex - 1] = entry;
+ indexVector[result.index] = entryIndex;
+ table[entryIndex - 1] = entry;
++m_keyCount;
}
-inline void PropertyTable::rehash(VM& vm, unsigned newCapacity)
+inline void PropertyTable::rehash(VM& vm, unsigned newCapacity, bool canStayCompact)
{
#if DUMP_PROPERTYMAP_STATS
++propertyTableStats->numRehashes;
#endif
- size_t oldDataSize = dataSize();
- unsigned* oldEntryIndices = m_index;
- iterator iter = this->begin();
- iterator end = this->end();
+ uintptr_t oldIndexVector = m_indexVector;
+ bool oldIsCompact = oldIndexVector & isCompactFlag;
+ unsigned oldIndexSize = m_indexSize;
+ unsigned oldUsedCount = usedCount();
+ size_t oldDataSize = dataSize(oldIsCompact, oldIndexSize);
m_indexSize = sizeForCapacity(newCapacity);
m_indexMask = m_indexSize - 1;
@@ -521,17 +561,28 @@
m_keyCount = 0;
m_deletedCount = 0;
- m_index = static_cast<unsigned*>(PropertyTableMalloc::zeroedMalloc(dataSize()));
+ // Once table gets non-compact, we do not change it back to compact again.
+ // This is because some of property offset can be larger than UINT8_MAX already.
+ bool isCompact = canStayCompact && oldIsCompact && tableCapacity() < UINT8_MAX;
+ m_indexVector = allocateZeroedIndexVector(isCompact, m_indexSize);
+ withIndexVector([&](auto* vector) {
+ auto* table = tableFromIndexVector(vector);
+ withIndexVector(oldIndexVector, [&](const auto* oldVector) {
+ const auto* oldCursor = tableFromIndexVector(oldVector, oldIndexSize);
+ const auto* oldEnd = oldCursor + oldUsedCount;
+ for (; oldCursor != oldEnd; ++oldCursor) {
+ if (oldCursor->key() == PROPERTY_MAP_DELETED_ENTRY_KEY)
+ continue;
+ ASSERT(canInsert(*oldCursor));
+ reinsert(vector, table, *oldCursor);
+ }
+ });
+ });
+ destroyIndexVector(oldIndexVector);
- for (; iter != end; ++iter) {
- ASSERT(canInsert());
- reinsert(*iter);
- }
-
- PropertyTableMalloc::free(oldEntryIndices);
-
- if (oldDataSize < dataSize())
- vm.heap.reportExtraMemoryAllocated(dataSize() - oldDataSize);
+ size_t newDataSize = dataSize(this->isCompact());
+ if (oldDataSize < newDataSize)
+ vm.heap.reportExtraMemoryAllocated(newDataSize - oldDataSize);
}
inline unsigned PropertyTable::tableCapacity() const { return m_indexSize >> 1; }
@@ -541,37 +592,46 @@
template<typename T>
inline T* PropertyTable::skipDeletedEntries(T* valuePtr, T* endValuePtr)
{
- while (valuePtr < endValuePtr && valuePtr->key == PROPERTY_MAP_DELETED_ENTRY_KEY)
+ while (valuePtr < endValuePtr && valuePtr->key() == PROPERTY_MAP_DELETED_ENTRY_KEY)
++valuePtr;
return valuePtr;
}
-inline PropertyTable::ValueType* PropertyTable::table()
+inline unsigned PropertyTable::usedCount() const
{
- // The table of values lies after the hash index.
- return reinterpret_cast_ptr<ValueType*>(m_index + m_indexSize);
+ // Total number of used entries in the values array - by either valid entries, or deleted ones.
+ return m_keyCount + m_deletedCount;
}
-inline const PropertyTable::ValueType* PropertyTable::table() const
+inline size_t PropertyTable::dataSize(bool isCompact, unsigned indexSize)
{
- // The table of values lies after the hash index.
- return reinterpret_cast_ptr<const ValueType*>(m_index + m_indexSize);
+ if (isCompact)
+ return indexSize * sizeof(uint8_t) + ((indexSize >> 1) + 1) * sizeof(CompactPropertyTableEntry);
+ return indexSize * sizeof(uint32_t) + ((indexSize >> 1) + 1) * sizeof(PropertyTableEntry);
}
-inline unsigned PropertyTable::usedCount() const
+inline size_t PropertyTable::dataSize(bool isCompact)
{
- // Total number of used entries in the values array - by either valid entries, or deleted ones.
- return m_keyCount + m_deletedCount;
+ // The size in bytes of data needed for by the table.
+ // Ensure that this function can be called concurrently.
+ return dataSize(isCompact, m_indexSize);
}
-inline size_t PropertyTable::dataSize()
+ALWAYS_INLINE uintptr_t PropertyTable::allocateIndexVector(bool isCompact, unsigned indexSize)
{
- // The size in bytes of data needed for by the table.
- // Ensure that this function can be called concurrently.
- unsigned indexSize = m_indexSize;
- return indexSize * sizeof(unsigned) + ((indexSize >> 1) + 1) * sizeof(ValueType);
+ return bitwise_cast<uintptr_t>(PropertyTableMalloc::malloc(PropertyTable::dataSize(isCompact, indexSize))) | (isCompact ? isCompactFlag : 0);
}
+ALWAYS_INLINE uintptr_t PropertyTable::allocateZeroedIndexVector(bool isCompact, unsigned indexSize)
+{
+ return bitwise_cast<uintptr_t>(PropertyTableMalloc::zeroedMalloc(PropertyTable::dataSize(isCompact, indexSize))) | (isCompact ? isCompactFlag : 0);
+}
+
+ALWAYS_INLINE void PropertyTable::destroyIndexVector(uintptr_t indexVector)
+{
+ PropertyTableMalloc::free(bitwise_cast<void*>(indexVector & indexVectorMask));
+}
+
inline unsigned PropertyTable::sizeForCapacity(unsigned capacity)
{
if (capacity < MinimumTableSize / 2)
@@ -579,9 +639,28 @@
return nextPowerOf2(capacity + 1) * 2;
}
-inline bool PropertyTable::canInsert()
+inline bool PropertyTable::canInsert(const ValueType& entry)
{
- return usedCount() < tableCapacity();
+ if (usedCount() >= tableCapacity())
+ return false;
+ if (!isCompact())
+ return true;
+ return canFitInCompact(entry);
}
+template<typename Functor>
+inline void PropertyTable::forEachProperty(const Functor& functor) const
+{
+ withIndexVector([&](const auto* vector) {
+ const auto* cursor = tableFromIndexVector(vector);
+ const auto* end = tableEndFromIndexVector(vector);
+ for (; cursor != end; ++cursor) {
+ if (cursor->key() == PROPERTY_MAP_DELETED_ENTRY_KEY)
+ continue;
+ if (functor(*cursor) == IterationStatus::Done)
+ return;
+ }
+ });
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/RegExpMatchesArray.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/RegExpMatchesArray.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/RegExpMatchesArray.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -30,11 +30,11 @@
namespace JSC {
-static const PropertyOffset RegExpMatchesArrayIndexPropertyOffset = 100;
-static const PropertyOffset RegExpMatchesArrayInputPropertyOffset = 101;
-static const PropertyOffset RegExpMatchesArrayGroupsPropertyOffset = 102;
-static const PropertyOffset RegExpMatchesArrayIndicesPropertyOffset = 103;
-static const PropertyOffset RegExpMatchesIndicesGroupsPropertyOffset = 100;
+static constexpr PropertyOffset RegExpMatchesArrayIndexPropertyOffset = firstOutOfLineOffset;
+static constexpr PropertyOffset RegExpMatchesArrayInputPropertyOffset = firstOutOfLineOffset + 1;
+static constexpr PropertyOffset RegExpMatchesArrayGroupsPropertyOffset = firstOutOfLineOffset + 2;
+static constexpr PropertyOffset RegExpMatchesArrayIndicesPropertyOffset = firstOutOfLineOffset + 3;
+static constexpr PropertyOffset RegExpMatchesIndicesGroupsPropertyOffset = firstOutOfLineOffset;
ALWAYS_INLINE JSArray* tryCreateUninitializedRegExpMatchesArray(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
{
Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/Structure.cpp 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp 2022-04-22 05:41:35 UTC (rev 293210)
@@ -106,8 +106,8 @@
unsigned numberLeaf = 0;
unsigned numberUsingSingleSlot = 0;
unsigned numberSingletons = 0;
- unsigned numberWithPropertyMaps = 0;
- unsigned totalPropertyMapsSize = 0;
+ unsigned numberWithPropertyTables = 0;
+ unsigned totalPropertyTablesSize = 0;
HashSet<Structure*>::const_iterator end = liveStructureSet.end();
for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
@@ -126,8 +126,8 @@
}
if (PropertyTable* table = structure->propertyTableOrNull()) {
- ++numberWithPropertyMaps;
- totalPropertyMapsSize += table->sizeInMemory();
+ ++numberWithPropertyTables;
+ totalPropertyTablesSize += table->sizeInMemory();
}
}
@@ -135,11 +135,11 @@
dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf);
dataLogF("Number of Structures that singletons: %d\n", numberSingletons);
- dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
+ dataLogF("Number of Structures with PropertyTables: %d\n", numberWithPropertyTables);
dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
- dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
- dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
+ dataLogF("Size of sum of all property maps: %d\n", totalPropertyTablesSize);
+ dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyTablesSize) / static_cast<double>(liveStructureSet.size()));
#else
dataLogF("Dumping Structure statistics is not enabled.\n");
#endif
@@ -225,7 +225,7 @@
setTransitionOffset(vm, invalidOffset);
setMaxOffset(vm, invalidOffset);
- ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity());
+ ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity);
ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset);
ASSERT(!hasRareData());
ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() == m_classInfo->hasStaticSetterOrReadonlyProperties());
@@ -416,25 +416,25 @@
continue;
switch (structure->transitionKind()) {
case TransitionKind::PropertyAddition: {
- PropertyMapEntry entry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes());
+ PropertyTableEntry entry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes());
auto nextOffset = table->nextOffset(structure->inlineCapacity());
ASSERT_UNUSED(nextOffset, nextOffset == structure->transitionOffset());
- auto result = table->add(vm, entry);
- ASSERT_UNUSED(result, result.second);
- ASSERT_UNUSED(result, result.first.first->offset == nextOffset);
+ auto [offset, attribute, result] = table->add(vm, entry);
+ ASSERT_UNUSED(result, result);
+ ASSERT_UNUSED(offset, offset == nextOffset);
+ UNUSED_VARIABLE(attribute);
break;
}
case TransitionKind::PropertyDeletion: {
- auto item = table->find(structure->m_transitionPropertyName.get());
- ASSERT(item.first);
- table->remove(vm, item);
+ auto [offset, attributes] = table->take(vm, structure->m_transitionPropertyName.get());
+ ASSERT_UNUSED(offset, offset != invalidOffset);
+ UNUSED_VARIABLE(attributes);
table->addDeletedOffset(structure->transitionOffset());
break;
}
case TransitionKind::PropertyAttributeChange: {
- PropertyMapEntry* entry = table->get(structure->m_transitionPropertyName.get());
- entry->attributes = structure->transitionPropertyAttributes();
- ASSERT(entry->offset == structure->transitionOffset());
+ PropertyOffset offset = table->updateAttributeIfExists(structure->m_transitionPropertyName.get(), structure->transitionPropertyAttributes());
+ ASSERT_UNUSED(offset, offset == structure->transitionOffset());
break;
}
default:
@@ -831,6 +831,8 @@
// table, since our logic for walking the property transition chain to rematerialize the
// table doesn't know how to take into account such wholesale edits.
+ ASSERT(transitionKind == TransitionKind::Seal || transitionKind == TransitionKind::Freeze);
+
PropertyTable* table = structure->copyPropertyTableForPinning(vm);
transition->pinForCaching(Locker { transition->m_lock }, vm, table);
transition->setMaxOffset(vm, structure->maxOffset());
@@ -837,12 +839,10 @@
table = transition->propertyTableOrNull();
RELEASE_ASSERT(table);
- for (auto& entry : *table) {
- if (setsDontDeleteOnAllProperties(transitionKind))
- entry.attributes |= static_cast<unsigned>(PropertyAttribute::DontDelete);
- if (setsReadOnlyOnNonAccessorProperties(transitionKind) && !(entry.attributes & PropertyAttribute::Accessor))
- entry.attributes |= static_cast<unsigned>(PropertyAttribute::ReadOnly);
- }
+ if (transitionKind == TransitionKind::Seal)
+ table->seal();
+ else
+ table->freeze();
} else {
transition->setPropertyTable(vm, structure->takePropertyTableOrCloneIfPinned(vm));
transition->setMaxOffset(vm, structure->maxOffset());
@@ -874,13 +874,7 @@
PropertyTable* table = ensurePropertyTableIfNotEmpty(vm);
if (!table)
return true;
-
- PropertyTable::iterator end = table->end();
- for (PropertyTable::iterator iter = table->begin(); iter != end; ++iter) {
- if ((iter->attributes & PropertyAttribute::DontDelete) != static_cast<unsigned>(PropertyAttribute::DontDelete))
- return false;
- }
- return true;
+ return table->isSealed();
}
// In future we may want to cache this property.
@@ -892,15 +886,7 @@
PropertyTable* table = ensurePropertyTableIfNotEmpty(vm);
if (!table)
return true;
-
- PropertyTable::iterator end = table->end();
- for (PropertyTable::iterator iter = table->begin(); iter != end; ++iter) {
- if (!(iter->attributes & PropertyAttribute::DontDelete))
- return false;
- if (!(iter->attributes & (PropertyAttribute::ReadOnly | PropertyAttribute::Accessor)))
- return false;
- }
- return true;
+ return table->isFrozen();
}
Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object)
@@ -921,17 +907,11 @@
size_t propertyCount = table->size();
- // Holds our values compacted by insertion order.
+ // Holds our values compacted by insertion order. This is OK since GC is deferred.
Vector<JSValue> values(propertyCount);
// Copies out our values from their hashed locations, compacting property table offsets as we go.
- unsigned i = 0;
- PropertyTable::iterator end = table->end();
- auto offset = invalidOffset;
- for (PropertyTable::iterator iter = table->begin(); iter != end; ++iter, ++i) {
- values[i] = object->getDirect(iter->offset);
- offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity);
- }
+ PropertyOffset offset = table->renumberPropertyOffsets(object, m_inlineCapacity, values);
setMaxOffset(vm, offset);
ASSERT(transitionOffset() == invalidOffset);
@@ -939,8 +919,6 @@
for (unsigned i = 0; i < propertyCount; i++)
object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]);
- table->clearDeletedOffsets();
-
// We need to zero our unused property space; otherwise the GC might see a
// stale pointer when we add properties in the future.
gcSafeZeroMemory(
@@ -1067,7 +1045,7 @@
{
unsigned finds = propertyTableStats->numFinds;
unsigned collisions = propertyTableStats->numCollisions;
- dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID());
+ dataLogF("\nJSC::PropertyTable 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", propertyTableStats->numLookups.load());
@@ -1137,9 +1115,10 @@
assertIsHeld(tableStructure->m_lock); // Sadly Clang needs some help here.
// Because uid is UniquedStringImpl, it is guaranteed that the hash is already computed.
// So we can use PropertyTable::get even from the concurrent compilers.
- if (auto* entry = table->get(uid)) {
- result = entry->offset;
- attributes = entry->attributes;
+ auto [offset, entryAttributes] = table->get(uid);
+ if (offset != invalidOffset) {
+ result = offset;
+ attributes = entryAttributes;
}
tableStructure->m_lock.unlock();
}
@@ -1147,12 +1126,12 @@
return result;
}
-Vector<PropertyMapEntry> Structure::getPropertiesConcurrently()
+Vector<PropertyTableEntry> Structure::getPropertiesConcurrently()
{
- Vector<PropertyMapEntry> result;
+ Vector<PropertyTableEntry> result;
forEachPropertyConcurrently(
- [&] (const PropertyMapEntry& entry) -> bool {
+ [&] (const PropertyTableEntry& entry) -> bool {
result.append(entry);
return true;
});
@@ -1194,34 +1173,35 @@
bool knownUnique = propertyNames.canAddKnownUniqueForStructure();
bool foundSymbol = false;
- auto checkDontEnumAndAdd = [&](PropertyTable::iterator iter) {
- if (mode == DontEnumPropertiesMode::Include || !(iter->attributes & PropertyAttribute::DontEnum)) {
+ auto checkDontEnumAndAdd = [&](const auto& entry) {
+ if (mode == DontEnumPropertiesMode::Include || !(entry.attributes() & PropertyAttribute::DontEnum)) {
if (knownUnique)
- propertyNames.addUnchecked(iter->key);
+ propertyNames.addUnchecked(entry.key());
else
- propertyNames.add(iter->key);
+ propertyNames.add(entry.key());
}
};
- PropertyTable::iterator end = table->end();
- for (PropertyTable::iterator iter = table->begin(); iter != end; ++iter) {
- ASSERT(!isQuickPropertyAccessAllowedForEnumeration() || !(iter->attributes & PropertyAttribute::DontEnum));
- ASSERT(!isQuickPropertyAccessAllowedForEnumeration() || !iter->key->isSymbol());
- if (iter->key->isSymbol()) {
+ table->forEachProperty([&](const auto& entry) {
+ ASSERT(!isQuickPropertyAccessAllowedForEnumeration() || !(entry.attributes() & PropertyAttribute::DontEnum));
+ ASSERT(!isQuickPropertyAccessAllowedForEnumeration() || !entry.key()->isSymbol());
+ if (entry.key()->isSymbol()) {
foundSymbol = true;
if (propertyNames.propertyNameMode() != PropertyNameMode::Symbols)
- continue;
+ return IterationStatus::Continue;
}
- checkDontEnumAndAdd(iter);
- }
+ checkDontEnumAndAdd(entry);
+ return IterationStatus::Continue;
+ });
if (foundSymbol && propertyNames.propertyNameMode() == PropertyNameMode::StringsAndSymbols) {
// To ensure the order defined in the spec, we append symbols at the last elements of keys.
// https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
- for (PropertyTable::iterator iter = table->begin(); iter != end; ++iter) {
- if (iter->key->isSymbol())
- checkDontEnumAndAdd(iter);
- }
+ table->forEachProperty([&](const auto& entry) {
+ if (entry.key()->isSymbol())
+ checkDontEnumAndAdd(entry);
+ return IterationStatus::Continue;
+ });
}
}
@@ -1344,9 +1324,9 @@
while (curStructure) {
sawPolyProtoStructure |= curStructure->hasPolyProto();
curStructure->forEachPropertyConcurrently(
- [&] (const PropertyMapEntry& entry) -> bool {
- if (!PropertyName(entry.key).isPrivateName())
- curShape->addProperty(*entry.key);
+ [&] (const PropertyTableEntry& entry) -> bool {
+ if (!PropertyName(entry.key()).isPrivateName())
+ curShape->addProperty(*entry.key());
return true;
});
@@ -1389,8 +1369,8 @@
CommaPrinter comma;
const_cast<Structure*>(this)->forEachPropertyConcurrently(
- [&] (const PropertyMapEntry& entry) -> bool {
- out.print(comma, entry.key, ":", static_cast<int>(entry.offset));
+ [&] (const PropertyTableEntry& entry) -> bool {
+ out.print(comma, entry.key(), ":", static_cast<int>(entry.offset()));
return true;
});
Modified: trunk/Source/_javascript_Core/runtime/Structure.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/Structure.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/Structure.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -45,6 +45,7 @@
#include "Watchpoint.h"
#include "WriteBarrierInlines.h"
#include <wtf/Atomics.h>
+#include <wtf/CompactPointerTuple.h>
#include <wtf/PrintStream.h>
namespace WTF {
@@ -77,27 +78,80 @@
// initial allocation.
static constexpr unsigned outOfLineGrowthFactor = 2;
-struct PropertyMapEntry {
- UniquedStringImpl* key;
- PropertyOffset offset;
- uint8_t attributes;
+class PropertyTableEntry;
+class CompactPropertyTableEntry {
+public:
+ CompactPropertyTableEntry()
+ : m_data(nullptr, 0)
+ {
+ }
- PropertyMapEntry()
- : key(nullptr)
- , offset(invalidOffset)
- , attributes(0)
+ CompactPropertyTableEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
+ : m_data(key, ((offset << 8) | attributes))
{
+ ASSERT(this->attributes() == attributes);
+ ASSERT(this->offset() == offset);
}
-
- PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
- : key(key)
- , offset(offset)
- , attributes(attributes)
+
+ CompactPropertyTableEntry(const PropertyTableEntry&);
+
+ UniquedStringImpl* key() const { return m_data.pointer(); }
+ void setKey(UniquedStringImpl* key) { m_data.setPointer(key); }
+ PropertyOffset offset() const { return m_data.type() >> 8; }
+ void setOffset(PropertyOffset offset)
{
- ASSERT(this->attributes == attributes);
+ m_data.setType((m_data.type() & 0x00ffU) | (offset << 8));
+ ASSERT(this->offset() == offset);
}
+ uint8_t attributes() const { return m_data.type(); }
+ void setAttributes(uint8_t attributes)
+ {
+ m_data.setType((m_data.type() & 0xff00U) | attributes);
+ ASSERT(this->attributes() == attributes);
+ }
+
+private:
+ CompactPointerTuple<UniquedStringImpl*, uint16_t> m_data;
};
+class PropertyTableEntry {
+public:
+ PropertyTableEntry() = default;
+
+ PropertyTableEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
+ : m_key(key)
+ , m_offset(offset)
+ , m_attributes(attributes)
+ {
+ ASSERT(this->attributes() == attributes);
+ }
+
+ PropertyTableEntry(const CompactPropertyTableEntry& entry)
+ : m_key(entry.key())
+ , m_offset(entry.offset())
+ , m_attributes(entry.attributes())
+ {
+ }
+
+ UniquedStringImpl* key() const { return m_key; }
+ void setKey(UniquedStringImpl* key) { m_key = key; }
+ PropertyOffset offset() const { return m_offset; }
+ void setOffset(PropertyOffset offset) { m_offset = offset; }
+ uint8_t attributes() const { return m_attributes; }
+ void setAttributes(uint8_t attributes) { m_attributes = attributes; }
+
+private:
+ UniquedStringImpl* m_key { nullptr };
+ PropertyOffset m_offset { 0 };
+ uint8_t m_attributes { 0 };
+};
+
+
+inline CompactPropertyTableEntry::CompactPropertyTableEntry(const PropertyTableEntry& entry)
+ : m_data(entry.key(), ((entry.offset() << 8) | entry.attributes()))
+{
+}
+
class StructureFireDetail final : public FireDetail {
public:
StructureFireDetail(const Structure* structure)
@@ -547,7 +601,7 @@
PropertyOffset getConcurrently(UniquedStringImpl* uid);
PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes);
- Vector<PropertyMapEntry> getPropertiesConcurrently();
+ Vector<PropertyTableEntry> getPropertiesConcurrently();
void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__)
{
Modified: trunk/Source/_javascript_Core/runtime/StructureInlines.h (293209 => 293210)
--- trunk/Source/_javascript_Core/runtime/StructureInlines.h 2022-04-22 05:35:33 UTC (rev 293209)
+++ trunk/Source/_javascript_Core/runtime/StructureInlines.h 2022-04-22 05:41:35 UTC (rev 293210)
@@ -171,12 +171,10 @@
if (!propertyTable)
return invalidOffset;
- PropertyMapEntry* entry = propertyTable->get(propertyName.uid());
- if (!entry)
- return invalidOffset;
-
- attributes = entry->attributes;
- return entry->offset;
+ auto [offset, entryAttributes] = propertyTable->get(propertyName.uid());
+ if (offset != invalidOffset)
+ attributes = entryAttributes;
+ return offset;
}
template<typename Functor>
@@ -208,7 +206,7 @@
break;
}
- if (!functor(PropertyMapEntry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes()))) {
+ if (!functor(PropertyTableEntry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes()))) {
if (didFindStructure) {
assertIsHeld(tableStructure->m_lock); // Sadly Clang needs some help here.
tableStructure->m_lock.unlock();
@@ -219,15 +217,15 @@
if (didFindStructure) {
assertIsHeld(tableStructure->m_lock); // Sadly Clang needs some help here.
- for (auto& entry : *table) {
- if (seenProperties.contains(entry.key))
- continue;
+ table->forEachProperty([&](const auto& entry) {
+ if (seenProperties.contains(entry.key()))
+ return IterationStatus::Continue;
- if (!functor(entry)) {
- tableStructure->m_lock.unlock();
- return;
- }
- }
+ if (!functor(entry))
+ return IterationStatus::Done;
+
+ return IterationStatus::Continue;
+ });
tableStructure->m_lock.unlock();
}
}
@@ -236,10 +234,11 @@
void Structure::forEachProperty(VM& vm, const Functor& functor)
{
if (PropertyTable* table = ensurePropertyTableIfNotEmpty(vm)) {
- for (auto& entry : *table) {
+ table->forEachProperty([&](const auto& entry) {
if (!functor(entry))
- return;
- }
+ return IterationStatus::Done;
+ return IterationStatus::Continue;
+ });
ensureStillAliveHere(table);
}
}
@@ -492,9 +491,10 @@
m_propertyHash = m_propertyHash ^ rep->existingSymbolAwareHash();
m_seenProperties.add(bitwise_cast<uintptr_t>(rep));
- auto result = table->add(vm, PropertyMapEntry(rep, newOffset, attributes));
- ASSERT_UNUSED(result, result.second);
- ASSERT_UNUSED(result, result.first.first->offset == newOffset);
+ auto [offset, attribute, result] = table->add(vm, PropertyTableEntry(rep, newOffset, attributes));
+ ASSERT_UNUSED(result, result);
+ ASSERT_UNUSED(offset, offset == newOffset);
+ UNUSED_VARIABLE(attribute);
auto newMaxOffset = std::max(newOffset, maxOffset());
func(locker, newOffset, newMaxOffset);
@@ -526,15 +526,13 @@
auto rep = propertyName.uid();
- PropertyTable::find_iterator position = table->find(rep);
- if (!position.first)
+ auto [offset, attributes] = table->take(vm, rep);
+ UNUSED_VARIABLE(attributes);
+ if (offset == invalidOffset)
return invalidOffset;
setIsQuickPropertyAccessAllowedForEnumeration(false);
-
- PropertyOffset offset = position.first->offset;
- table->remove(vm, position);
table->addDeletedOffset(offset);
PropertyOffset newMaxOffset = maxOffset();
@@ -567,19 +565,15 @@
ASSERT(JSC::isValidOffset(get(vm, propertyName)));
checkConsistency();
- PropertyMapEntry* entry = table->get(propertyName.uid());
- if (!entry)
- return invalidOffset;
+ PropertyOffset offset = table->updateAttributeIfExists(propertyName.uid(), attributes);
+ if (offset == invalidOffset)
+ return offset;
- PropertyOffset offset = entry->offset;
-
if (attributes & PropertyAttribute::DontEnum)
setIsQuickPropertyAccessAllowedForEnumeration(false);
if (attributes & PropertyAttribute::ReadOnly)
setContainsReadOnlyProperties();
- entry->attributes = attributes;
-
PropertyOffset newMaxOffset = maxOffset();
func(locker, offset, newMaxOffset);