Structure should be more methodical about the relationship between m_offset and m_propertyTable
https://bugs.webkit.org/show_bug.cgi?id=109978
Reviewed by Mark Hahnenberg.
Allegedly, the previous relationship was that either m_propertyTable or m_offset
would be set, and if m_propertyTable was not set you could rebuild it. In reality,
we would sometimes "reset" both: some transitions wouldn't set m_offset, and other
transitions would clear the previous structure's m_propertyTable. So, in a
structure transition chain of A->B->C you could have:
A transitions to B: B doesn't copy m_offset but does copy m_propertyTable, because
that seemed like a good idea at the time (this was a common idiom in the code).
B transitions to C: C steals B's m_propertyTable, leaving B with neither a
m_propertyTable nor a m_offset.
Then we would ask for the size of the property storage of B and get the answer
"none". That's not good.
Now, there is a new relationship, which, hopefully, should fix things: m_offset is
always set and always refers to the maximum offset ever used by the property table.
From this, you can infer both the inline and out-of-line property size, and
capacity. This is accomplished by having PropertyTable::add() take a
PropertyOffset reference, which must be Structure::m_offset. It will update this
offset. As well, all transitions now copy m_offset. And we frequently assert
(using RELEASE_ASSERT) that the m_offset matches what m_propertyTable would tell
you. Hence if you ever modify the m_propertyTable, you'll also update the offset.
If you ever copy the property table, you'll also copy the offset. Life should be
good, I think.
* runtime/PropertyMapHashTable.h:
(JSC::PropertyTable::add):
* runtime/Structure.cpp:
(JSC::Structure::materializePropertyMap):
(JSC::Structure::addPropertyTransition):
(JSC::Structure::removePropertyTransition):
(JSC::Structure::changePrototypeTransition):
(JSC::Structure::despecifyFunctionTransition):
(JSC::Structure::attributeChangeTransition):
(JSC::Structure::toDictionaryTransition):
(JSC::Structure::sealTransition):
(JSC::Structure::freezeTransition):
(JSC::Structure::preventExtensionsTransition):
(JSC::Structure::nonPropertyTransition):
(JSC::Structure::flattenDictionaryStructure):
(JSC::Structure::checkConsistency):
(JSC::Structure::putSpecificValue):
(JSC::Structure::createPropertyMap):
(JSC::PropertyTable::checkConsistency):
* runtime/Structure.h:
(JSC):
(JSC::Structure::putWillGrowOutOfLineStorage):
(JSC::Structure::outOfLineCapacity):
(JSC::Structure::outOfLineSize):
(JSC::Structure::isEmpty):
(JSC::Structure::materializePropertyMapIfNecessary):
(JSC::Structure::materializePropertyMapIfNecessaryForPinning):
(Structure):
(JSC::Structure::checkOffsetConsistency):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@143097 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 0c3acff..926275d 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,65 @@
+2013-02-15 Filip Pizlo <fpizlo@apple.com>
+
+ Structure should be more methodical about the relationship between m_offset and m_propertyTable
+ https://bugs.webkit.org/show_bug.cgi?id=109978
+
+ Reviewed by Mark Hahnenberg.
+
+ Allegedly, the previous relationship was that either m_propertyTable or m_offset
+ would be set, and if m_propertyTable was not set you could rebuild it. In reality,
+ we would sometimes "reset" both: some transitions wouldn't set m_offset, and other
+ transitions would clear the previous structure's m_propertyTable. So, in a
+ structure transition chain of A->B->C you could have:
+
+ A transitions to B: B doesn't copy m_offset but does copy m_propertyTable, because
+ that seemed like a good idea at the time (this was a common idiom in the code).
+ B transitions to C: C steals B's m_propertyTable, leaving B with neither a
+ m_propertyTable nor a m_offset.
+
+ Then we would ask for the size of the property storage of B and get the answer
+ "none". That's not good.
+
+ Now, there is a new relationship, which, hopefully, should fix things: m_offset is
+ always set and always refers to the maximum offset ever used by the property table.
+ From this, you can infer both the inline and out-of-line property size, and
+ capacity. This is accomplished by having PropertyTable::add() take a
+ PropertyOffset reference, which must be Structure::m_offset. It will update this
+ offset. As well, all transitions now copy m_offset. And we frequently assert
+ (using RELEASE_ASSERT) that the m_offset matches what m_propertyTable would tell
+ you. Hence if you ever modify the m_propertyTable, you'll also update the offset.
+ If you ever copy the property table, you'll also copy the offset. Life should be
+ good, I think.
+
+ * runtime/PropertyMapHashTable.h:
+ (JSC::PropertyTable::add):
+ * runtime/Structure.cpp:
+ (JSC::Structure::materializePropertyMap):
+ (JSC::Structure::addPropertyTransition):
+ (JSC::Structure::removePropertyTransition):
+ (JSC::Structure::changePrototypeTransition):
+ (JSC::Structure::despecifyFunctionTransition):
+ (JSC::Structure::attributeChangeTransition):
+ (JSC::Structure::toDictionaryTransition):
+ (JSC::Structure::sealTransition):
+ (JSC::Structure::freezeTransition):
+ (JSC::Structure::preventExtensionsTransition):
+ (JSC::Structure::nonPropertyTransition):
+ (JSC::Structure::flattenDictionaryStructure):
+ (JSC::Structure::checkConsistency):
+ (JSC::Structure::putSpecificValue):
+ (JSC::Structure::createPropertyMap):
+ (JSC::PropertyTable::checkConsistency):
+ * runtime/Structure.h:
+ (JSC):
+ (JSC::Structure::putWillGrowOutOfLineStorage):
+ (JSC::Structure::outOfLineCapacity):
+ (JSC::Structure::outOfLineSize):
+ (JSC::Structure::isEmpty):
+ (JSC::Structure::materializePropertyMapIfNecessary):
+ (JSC::Structure::materializePropertyMapIfNecessaryForPinning):
+ (Structure):
+ (JSC::Structure::checkOffsetConsistency):
+
2013-02-15 Martin Robinson <mrobinson@igalia.com>
[GTK] Spread the gyp build files throughout the tree
diff --git a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
index 966c199..2422c5b 100644
--- a/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
+++ b/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
@@ -157,7 +157,8 @@
find_iterator find(const KeyType&);
find_iterator findWithString(const KeyType&);
// Add a value to the table
- std::pair<find_iterator, bool> add(const ValueType& entry);
+ enum EffectOnPropertyOffset { PropertyOffsetMayChange, PropertyOffsetMustNotChange };
+ std::pair<find_iterator, bool> add(const ValueType& entry, PropertyOffset&, EffectOnPropertyOffset);
// Remove a value from the table.
void remove(const find_iterator& iter);
void remove(const KeyType& key);
@@ -389,12 +390,14 @@
}
}
-inline std::pair<PropertyTable::find_iterator, bool> PropertyTable::add(const ValueType& entry)
+inline std::pair<PropertyTable::find_iterator, bool> PropertyTable::add(const ValueType& entry, PropertyOffset& offset, EffectOnPropertyOffset offsetEffect)
{
// Look for a value with a matching key already in the array.
find_iterator iter = find(entry.key);
- if (iter.first)
+ if (iter.first) {
+ RELEASE_ASSERT(iter.first->offset <= offset);
return std::make_pair(iter, false);
+ }
// Ref the key
entry.key->ref();
@@ -413,6 +416,12 @@
*iter.first = entry;
++m_keyCount;
+
+ if (offsetEffect == PropertyOffsetMayChange)
+ offset = std::max(offset, entry.offset);
+ else
+ RELEASE_ASSERT(offset >= entry.offset);
+
return std::make_pair(iter, true);
}
diff --git a/Source/JavaScriptCore/runtime/Structure.cpp b/Source/JavaScriptCore/runtime/Structure.cpp
index 8650a7f..6160232 100644
--- a/Source/JavaScriptCore/runtime/Structure.cpp
+++ b/Source/JavaScriptCore/runtime/Structure.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009 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
@@ -256,13 +256,15 @@
if (!m_propertyTable)
createPropertyMap(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity));
- for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
+ for (ptrdiff_t i = structures.size() - 1; i >= 0; --i) {
structure = structures[i];
if (!structure->m_nameInPrevious)
continue;
PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get());
- m_propertyTable->add(entry);
+ m_propertyTable->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange);
}
+
+ checkOffsetConsistency();
}
inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity)
@@ -371,6 +373,7 @@
transition->m_specificValueInPrevious.setMayBeNull(globalData, transition, specificValue);
if (structure->m_propertyTable) {
+ structure->checkOffsetConsistency();
if (structure->m_isPinnedPropertyTable)
transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1);
else
@@ -381,12 +384,14 @@
else
transition->createPropertyMap();
}
+ transition->m_offset = structure->m_offset;
offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
- transition->m_offset = offset;
checkOffset(transition->m_offset, transition->inlineCapacity());
structure->m_transitionTable.add(globalData, transition);
+ transition->checkOffsetConsistency();
+ structure->checkOffsetConsistency();
return transition;
}
@@ -398,6 +403,7 @@
offset = transition->remove(propertyName);
+ transition->checkOffsetConsistency();
return transition;
}
@@ -407,12 +413,12 @@
transition->m_prototype.set(globalData, transition, prototype);
- // Don't set m_offset, as one can not transition to this.
-
structure->materializePropertyMapIfNecessary(globalData);
transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
+ transition->m_offset = structure->m_offset;
transition->pin();
+ transition->checkOffsetConsistency();
return transition;
}
@@ -423,10 +429,9 @@
++transition->m_specificFunctionThrashCount;
- // Don't set m_offset, as one can not transition to this.
-
structure->materializePropertyMapIfNecessary(globalData);
transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
+ transition->m_offset = structure->m_offset;
transition->pin();
if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
@@ -436,6 +441,7 @@
ASSERT_UNUSED(removed, removed);
}
+ transition->checkOffsetConsistency();
return transition;
}
@@ -444,10 +450,9 @@
if (!structure->isUncacheableDictionary()) {
Structure* transition = create(globalData, structure);
- // Don't set m_offset, as one can not transition to this.
-
structure->materializePropertyMapIfNecessary(globalData);
transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
+ transition->m_offset = structure->m_offset;
transition->pin();
structure = transition;
@@ -458,6 +463,7 @@
ASSERT(entry);
entry->attributes = attributes;
+ structure->checkOffsetConsistency();
return structure;
}
@@ -469,9 +475,11 @@
structure->materializePropertyMapIfNecessary(globalData);
transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
+ transition->m_offset = structure->m_offset;
transition->m_dictionaryKind = kind;
transition->pin();
+ transition->checkOffsetConsistency();
return transition;
}
@@ -496,6 +504,7 @@
iter->attributes |= DontDelete;
}
+ transition->checkOffsetConsistency();
return transition;
}
@@ -513,6 +522,7 @@
iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly);
}
+ transition->checkOffsetConsistency();
return transition;
}
@@ -525,9 +535,11 @@
structure->materializePropertyMapIfNecessary(globalData);
transition->m_propertyTable = structure->copyPropertyTableForPinning(globalData, transition);
+ transition->m_offset = structure->m_offset;
transition->m_preventExtensions = true;
transition->pin();
+ transition->checkOffsetConsistency();
return transition;
}
@@ -560,6 +572,7 @@
checkOffset(transition->m_offset, transition->inlineCapacity());
if (structure->m_propertyTable) {
+ structure->checkOffsetConsistency();
if (structure->m_isPinnedPropertyTable)
transition->m_propertyTable = structure->m_propertyTable->copy(globalData, transition, structure->m_propertyTable->size() + 1);
else
@@ -572,6 +585,7 @@
}
structure->m_transitionTable.add(globalData, transition);
+ transition->checkOffsetConsistency();
return transition;
}
@@ -615,6 +629,7 @@
Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObject* object)
{
+ checkOffsetConsistency();
ASSERT(isDictionary());
if (isUncacheableDictionary()) {
ASSERT(m_propertyTable);
@@ -629,7 +644,7 @@
PropertyTable::iterator end = m_propertyTable->end();
for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) {
values[i] = object->getDirect(iter->offset);
- iter->offset = offsetForPropertyNumber(i, m_inlineCapacity);
+ m_offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity);
}
// Copies in our values to their compacted locations.
@@ -637,6 +652,7 @@
object->putDirect(globalData, offsetForPropertyNumber(i, m_inlineCapacity), values[i]);
m_propertyTable->clearDeletedOffsets();
+ checkOffsetConsistency();
}
m_dictionaryKind = NoneDictionaryKind;
@@ -714,6 +730,7 @@
inline void Structure::checkConsistency()
{
+ checkOffsetConsistency();
}
#endif
@@ -786,8 +803,8 @@
PropertyOffset newOffset = m_propertyTable->nextOffset(m_inlineCapacity);
- m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue));
-
+ m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue), m_offset, PropertyTable::PropertyOffsetMayChange);
+
checkConsistency();
return newOffset;
}
@@ -820,7 +837,6 @@
checkConsistency();
m_propertyTable = adoptPtr(new PropertyTable(capacity));
- checkConsistency();
}
void Structure::getPropertyNamesFromStructure(JSGlobalData& globalData, PropertyNameArray& propertyNames, EnumerationMode mode)
@@ -896,6 +912,7 @@
void PropertyTable::checkConsistency()
{
+ checkOffsetConsistency();
ASSERT(m_indexSize >= PropertyTable::MinimumTableSize);
ASSERT(m_indexMask);
ASSERT(m_indexSize == m_indexMask + 1);
diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h
index 8f42078..628461f 100644
--- a/Source/JavaScriptCore/runtime/Structure.h
+++ b/Source/JavaScriptCore/runtime/Structure.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012, 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
@@ -110,6 +110,8 @@
bool didTransition() const { return m_didTransition; }
bool putWillGrowOutOfLineStorage()
{
+ checkOffsetConsistency();
+
ASSERT(outOfLineCapacity() >= outOfLineSize());
if (!m_propertyTable) {
@@ -187,6 +189,8 @@
unsigned outOfLineCapacity() const
{
+ ASSERT(checkOffsetConsistency());
+
unsigned outOfLineSize = this->outOfLineSize();
if (!outOfLineSize)
@@ -201,14 +205,9 @@
}
unsigned outOfLineSize() const
{
+ ASSERT(checkOffsetConsistency());
ASSERT(structure()->classInfo() == &s_info);
- if (m_propertyTable) {
- unsigned totalSize = m_propertyTable->propertyStorageSize();
- unsigned inlineCapacity = this->inlineCapacity();
- if (totalSize < inlineCapacity)
- return 0;
- return totalSize - inlineCapacity;
- }
+
return numberOfOutOfLineSlotsForLastOffset(m_offset);
}
bool hasInlineStorage() const
@@ -221,17 +220,10 @@
}
unsigned inlineSize() const
{
- unsigned result;
- if (m_propertyTable)
- result = m_propertyTable->propertyStorageSize();
- else
- result = m_offset + 1;
- return std::min<unsigned>(result, m_inlineCapacity);
+ return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
}
unsigned totalStorageSize() const
{
- if (m_propertyTable)
- return m_propertyTable->propertyStorageSize();
return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
}
unsigned totalStorageCapacity() const
@@ -248,8 +240,6 @@
}
PropertyOffset lastValidOffset() const
{
- if (m_propertyTable)
- return offsetForPropertyNumber(m_propertyTable->propertyStorageSize() - 1, m_inlineCapacity);
return m_offset;
}
bool isValidOffset(PropertyOffset offset) const
@@ -281,8 +271,7 @@
bool isEmpty() const
{
- if (m_propertyTable)
- return m_propertyTable->isEmpty();
+ ASSERT(checkOffsetConsistency());
return !JSC::isValidOffset(m_offset);
}
@@ -405,12 +394,14 @@
void materializePropertyMapIfNecessary(JSGlobalData& globalData)
{
ASSERT(structure()->classInfo() == &s_info);
+ ASSERT(checkOffsetConsistency());
if (!m_propertyTable && previousID())
materializePropertyMap(globalData);
}
void materializePropertyMapIfNecessaryForPinning(JSGlobalData& globalData)
{
ASSERT(structure()->classInfo() == &s_info);
+ checkOffsetConsistency();
if (!m_propertyTable)
materializePropertyMap(globalData);
}
@@ -453,6 +444,20 @@
ASSERT(typeInfo().structureHasRareData());
return static_cast<StructureRareData*>(m_previousOrRareData.get());
}
+
+ ALWAYS_INLINE bool checkOffsetConsistency() const
+ {
+ if (!m_propertyTable) {
+ ASSERT(!m_isPinnedPropertyTable);
+ return true;
+ }
+
+ RELEASE_ASSERT(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) == m_propertyTable->propertyStorageSize());
+ unsigned totalSize = m_propertyTable->propertyStorageSize();
+ RELEASE_ASSERT((totalSize < inlineCapacity() ? 0 : totalSize - inlineCapacity()) == numberOfOutOfLineSlotsForLastOffset(m_offset));
+
+ return true;
+ }
void allocateRareData(JSGlobalData&);
void cloneRareDataFrom(JSGlobalData&, const Structure*);