| Index: third_party/WebKit/Source/wtf/HashTable.h
|
| diff --git a/third_party/WebKit/Source/wtf/HashTable.h b/third_party/WebKit/Source/wtf/HashTable.h
|
| index a1b96bc013b4352635951b34ab8a47f4d793831e..d43dc6cb63e1d316023172dbc294dc3f822c30fd 100644
|
| --- a/third_party/WebKit/Source/wtf/HashTable.h
|
| +++ b/third_party/WebKit/Source/wtf/HashTable.h
|
| @@ -103,6 +103,29 @@ class LinkedHashSet;
|
| template <WeakHandlingFlag x, typename T, typename U, typename V, typename W, typename X, typename Y, typename Z>
|
| struct WeakProcessingHashTableHelper;
|
|
|
| +#if ENABLE(ASSERT)
|
| +// HashTable and collections that build on it do not support modifications
|
| +// while there is an iterator or HashTableAddResult on the stack.
|
| +// The exception is ListHashSet, which has its own iterators that tolerate modification
|
| +// of the underlying set.
|
| +//
|
| +// Constraint enforced by this scope object.
|
| +template <typename HashTableType>
|
| +class HashTableModificationForbiddenScope {
|
| +public:
|
| + HashTableModificationForbiddenScope();
|
| + explicit HashTableModificationForbiddenScope(const HashTableType*);
|
| + HashTableModificationForbiddenScope(const HashTableModificationForbiddenScope&);
|
| +
|
| + ~HashTableModificationForbiddenScope();
|
| +
|
| + void leaveScope();
|
| +
|
| +private:
|
| + HashTableType* m_container;
|
| +};
|
| +#endif
|
| +
|
| typedef enum { HashItemKnownGood } HashItemKnownGoodTag;
|
|
|
| template <typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename Allocator>
|
| @@ -129,8 +152,7 @@ private:
|
| : m_position(position)
|
| , m_endPosition(endPosition)
|
| #if ENABLE(ASSERT)
|
| - , m_container(container)
|
| - , m_containerModifications(container->modifications())
|
| + , m_modificationForbiddenScope(position != endPosition ? container : nullptr)
|
| #endif
|
| {
|
| skipEmptyBuckets();
|
| @@ -140,21 +162,9 @@ private:
|
| : m_position(position)
|
| , m_endPosition(endPosition)
|
| #if ENABLE(ASSERT)
|
| - , m_container(container)
|
| - , m_containerModifications(container->modifications())
|
| + , m_modificationForbiddenScope(position != endPosition ? container : nullptr)
|
| #endif
|
| {
|
| - ASSERT(m_containerModifications == m_container->modifications());
|
| - }
|
| -
|
| - void checkModifications() const
|
| - {
|
| - // HashTable and collections that build on it do not support
|
| - // modifications while there is an iterator in use. The exception is
|
| - // ListHashSet, which has its own iterators that tolerate modification
|
| - // of the underlying set.
|
| - ASSERT(m_containerModifications == m_container->modifications());
|
| - ASSERT(!m_container->accessForbidden());
|
| }
|
|
|
| public:
|
| @@ -162,7 +172,6 @@ public:
|
|
|
| GetType get() const
|
| {
|
| - checkModifications();
|
| return m_position;
|
| }
|
| typename Traits::IteratorConstReferenceType operator*() const { return Traits::getToReferenceConstConversion(get()); }
|
| @@ -171,9 +180,15 @@ public:
|
| const_iterator& operator++()
|
| {
|
| ASSERT(m_position != m_endPosition);
|
| - checkModifications();
|
| ++m_position;
|
| skipEmptyBuckets();
|
| +#if ENABLE(ASSERT)
|
| + // If now at the end, leave the forbidden scope. Waiting until
|
| + // the iterator goes out of (stack) scope disallows perfectly valid
|
| + // code idioms.
|
| + if (m_position == m_endPosition)
|
| + m_modificationForbiddenScope.leaveScope();
|
| +#endif
|
| return *this;
|
| }
|
|
|
| @@ -201,8 +216,7 @@ private:
|
| PointerType m_position;
|
| PointerType m_endPosition;
|
| #if ENABLE(ASSERT)
|
| - const HashTableType* m_container;
|
| - int64_t m_containerModifications;
|
| + HashTableModificationForbiddenScope<HashTableType> m_modificationForbiddenScope;
|
| #endif
|
| };
|
|
|
| @@ -277,14 +291,14 @@ public:
|
| template <typename T, typename U, typename V> static void translate(T& location, U&&, V&& value) { location = std::forward<V>(value); }
|
| };
|
|
|
| -template <typename HashTableType, typename ValueType> struct HashTableAddResult final {
|
| +template <typename HashTableType, typename ValueType>
|
| +struct HashTableAddResult final {
|
| STACK_ALLOCATED();
|
| HashTableAddResult(const HashTableType* container, ValueType* storedValue, bool isNewEntry)
|
| : storedValue(storedValue)
|
| , isNewEntry(isNewEntry)
|
| #if ENABLE(SECURITY_ASSERT)
|
| - , m_container(container)
|
| - , m_containerModifications(container->modifications())
|
| + , m_modificationForbiddenScope(container)
|
| #endif
|
| {
|
| ALLOW_UNUSED_LOCAL(container);
|
| @@ -298,18 +312,12 @@ template <typename HashTableType, typename ValueType> struct HashTableAddResult
|
| ~HashTableAddResult()
|
| {
|
| // If rehash happened before accessing storedValue, it's
|
| - // use-after-free. Any modification may cause a rehash, so we check for
|
| - // modifications here.
|
| -
|
| - // Rehash after accessing storedValue is harmless but will assert if the
|
| - // AddResult destructor takes place after a modification. You may need
|
| - // to limit the scope of the AddResult.
|
| - ASSERT_WITH_SECURITY_IMPLICATION(m_containerModifications == m_container->modifications());
|
| + // use-after-free. Any modification may cause a rehash, so we enter
|
| + // a modification forbidden scope for the lifetime of this value.
|
| }
|
|
|
| private:
|
| - const HashTableType* m_container;
|
| - const int64_t m_containerModifications;
|
| + HashTableModificationForbiddenScope<HashTableType> m_modificationForbiddenScope;
|
| #endif
|
| };
|
|
|
| @@ -413,14 +421,7 @@ public:
|
| ASSERT(!Allocator::isGarbageCollected);
|
| if (LIKELY(!m_table))
|
| return;
|
| - ASSERT(!m_accessForbidden);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = true;
|
| -#endif
|
| deleteAllBucketsAndDeallocate(m_table, m_tableSize);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = false;
|
| -#endif
|
| m_table = nullptr;
|
| }
|
|
|
| @@ -441,17 +442,14 @@ public:
|
|
|
| unsigned size() const
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| return m_keyCount;
|
| }
|
| unsigned capacity() const
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| return m_tableSize;
|
| }
|
| bool isEmpty() const
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| return !m_keyCount;
|
| }
|
|
|
| @@ -493,17 +491,19 @@ public:
|
| template <typename VisitorDispatcher> void trace(VisitorDispatcher);
|
|
|
| #if ENABLE(ASSERT)
|
| - bool accessForbidden() const { return m_accessForbidden; }
|
| - int64_t modifications() const { return m_modifications; }
|
| - void registerModification() { m_modifications++; }
|
| - // HashTable and collections that build on it do not support modifications
|
| - // while there is an iterator in use. The exception is ListHashSet, which
|
| - // has its own iterators that tolerate modification of the underlying set.
|
| - void checkModifications(int64_t mods) const { ASSERT(mods == m_modifications); }
|
| + void enterModificationForbiddenScope() { m_modificationForbidden++; }
|
| + void leaveModificationForbiddenScope()
|
| + {
|
| + ASSERT(m_modificationForbidden > 0);
|
| + m_modificationForbidden--;
|
| + }
|
| +
|
| + void registerModification()
|
| + {
|
| + ASSERT(!m_modificationForbidden);
|
| + }
|
| #else
|
| - int64_t modifications() const { return 0; }
|
| - void registerModification() {}
|
| - void checkModifications(int64_t mods) const {}
|
| + void registerModification() { }
|
| #endif
|
|
|
| private:
|
| @@ -569,14 +569,10 @@ private:
|
| ValueType* m_table;
|
| unsigned m_tableSize;
|
| unsigned m_keyCount;
|
| -#if ENABLE(ASSERT)
|
| - unsigned m_deletedCount:30;
|
| - unsigned m_queueFlag:1;
|
| - unsigned m_accessForbidden:1;
|
| - unsigned m_modifications;
|
| -#else
|
| unsigned m_deletedCount:31;
|
| unsigned m_queueFlag:1;
|
| +#if ENABLE(ASSERT)
|
| + unsigned m_modificationForbidden;
|
| #endif
|
|
|
| #if DUMP_HASHTABLE_STATS_PER_TABLE
|
| @@ -596,8 +592,7 @@ inline HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Alloca
|
| , m_deletedCount(0)
|
| , m_queueFlag(false)
|
| #if ENABLE(ASSERT)
|
| - , m_accessForbidden(false)
|
| - , m_modifications(0)
|
| + , m_modificationForbidden(0)
|
| #endif
|
| #if DUMP_HASHTABLE_STATS_PER_TABLE
|
| , m_stats(adoptPtr(new Stats))
|
| @@ -647,7 +642,6 @@ template <typename Key, typename Value, typename Extractor, typename HashFunctio
|
| template <typename HashTranslator, typename T>
|
| inline const Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::lookup(const T& key) const
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| ASSERT((HashTableKeyChecker<HashTranslator, KeyTraits, HashFunctions::safeToCompareToEmptyOrDeleted>::checkKey(key)));
|
| const ValueType* table = m_table;
|
| if (!table)
|
| @@ -687,7 +681,6 @@ template <typename Key, typename Value, typename Extractor, typename HashFunctio
|
| template <typename HashTranslator, typename T>
|
| inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::LookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::lookupForWriting(const T& key)
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| ASSERT(m_table);
|
| registerModification();
|
|
|
| @@ -730,7 +723,6 @@ template <typename Key, typename Value, typename Extractor, typename HashFunctio
|
| template <typename HashTranslator, typename T>
|
| inline typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::FullLookupType HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::fullLookupForWriting(const T& key)
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| ASSERT(m_table);
|
| registerModification();
|
|
|
| @@ -801,7 +793,6 @@ template <typename Key, typename Value, typename Extractor, typename HashFunctio
|
| template <typename HashTranslator, typename T, typename Extra>
|
| typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::AddResult HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::add(T&& key, Extra&& extra)
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| ASSERT(Allocator::isAllocationAllowed());
|
| if (!m_table)
|
| expand();
|
| @@ -867,7 +858,6 @@ template <typename Key, typename Value, typename Extractor, typename HashFunctio
|
| template <typename HashTranslator, typename T, typename Extra>
|
| typename HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::AddResult HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::addPassingHashCode(T&& key, Extra&& extra)
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| ASSERT(Allocator::isAllocationAllowed());
|
| if (!m_table)
|
| expand();
|
| @@ -957,14 +947,7 @@ void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocato
|
| ++m_stats->numRemoves;
|
| #endif
|
|
|
| - ASSERT(!m_accessForbidden);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = true;
|
| -#endif
|
| deleteBucket(*pos);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = false;
|
| -#endif
|
| ++m_deletedCount;
|
| --m_keyCount;
|
|
|
| @@ -1100,15 +1083,7 @@ Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Alloca
|
| }
|
| newEntry = rehashTo(originalTable, newTableSize, newEntry);
|
|
|
| - ASSERT(!m_accessForbidden);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = true;
|
| -#endif
|
| deleteAllBucketsAndDeallocate(temporaryTable, oldTableSize);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = false;
|
| -#endif
|
| -
|
| return newEntry;
|
| }
|
|
|
| @@ -1178,15 +1153,7 @@ Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Alloca
|
| ValueType* newTable = allocateTable(newTableSize);
|
| Value* newEntry = rehashTo(newTable, newTableSize, entry);
|
|
|
| - ASSERT(!m_accessForbidden);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = true;
|
| -#endif
|
| deleteAllBucketsAndDeallocate(oldTable, oldTableSize);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = false;
|
| -#endif
|
| -
|
| return newEntry;
|
| }
|
|
|
| @@ -1197,14 +1164,7 @@ void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocato
|
| if (!m_table)
|
| return;
|
|
|
| - ASSERT(!m_accessForbidden);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = true;
|
| -#endif
|
| deleteAllBucketsAndDeallocate(m_table, m_tableSize);
|
| -#if ENABLE(ASSERT)
|
| - m_accessForbidden = false;
|
| -#endif
|
| m_table = nullptr;
|
| m_tableSize = 0;
|
| m_keyCount = 0;
|
| @@ -1218,8 +1178,7 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::H
|
| , m_deletedCount(0)
|
| , m_queueFlag(false)
|
| #if ENABLE(ASSERT)
|
| - , m_accessForbidden(false)
|
| - , m_modifications(0)
|
| + , m_modificationForbidden(0)
|
| #endif
|
| #if DUMP_HASHTABLE_STATS_PER_TABLE
|
| , m_stats(adoptPtr(new Stats(*other.m_stats)))
|
| @@ -1241,8 +1200,7 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::H
|
| , m_deletedCount(0)
|
| , m_queueFlag(false)
|
| #if ENABLE(ASSERT)
|
| - , m_accessForbidden(false)
|
| - , m_modifications(0)
|
| + , m_modificationForbidden(0)
|
| #endif
|
| #if DUMP_HASHTABLE_STATS_PER_TABLE
|
| , m_stats(adoptPtr(new Stats(*other.m_stats)))
|
| @@ -1255,7 +1213,7 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::H
|
| template <typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename Allocator>
|
| void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::swap(HashTable& other)
|
| {
|
| - ASSERT(!m_accessForbidden);
|
| + registerModification();
|
| std::swap(m_table, other.m_table);
|
| std::swap(m_tableSize, other.m_tableSize);
|
| std::swap(m_keyCount, other.m_keyCount);
|
| @@ -1267,7 +1225,7 @@ void HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocato
|
| ASSERT(!other.m_queueFlag);
|
|
|
| #if ENABLE(ASSERT)
|
| - std::swap(m_modifications, other.m_modifications);
|
| + std::swap(m_modificationForbidden, other.m_modificationForbidden);
|
| #endif
|
|
|
| #if DUMP_HASHTABLE_STATS_PER_TABLE
|
| @@ -1290,6 +1248,45 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>& H
|
| return *this;
|
| }
|
|
|
| +#if ENABLE(ASSERT)
|
| +template <typename HashTableType>
|
| +HashTableModificationForbiddenScope<HashTableType>::HashTableModificationForbiddenScope()
|
| + : m_container(nullptr)
|
| +{
|
| +}
|
| +
|
| +template <typename HashTableType>
|
| +HashTableModificationForbiddenScope<HashTableType>::HashTableModificationForbiddenScope(const HashTableType* container)
|
| + : m_container(const_cast<HashTableType*>(container))
|
| +{
|
| + if (m_container)
|
| + m_container->enterModificationForbiddenScope();
|
| +}
|
| +
|
| +template <typename HashTableType>
|
| +HashTableModificationForbiddenScope<HashTableType>::HashTableModificationForbiddenScope(const HashTableModificationForbiddenScope& other)
|
| + : m_container(other.m_container)
|
| +{
|
| + if (m_container)
|
| + m_container->enterModificationForbiddenScope();
|
| +}
|
| +
|
| +template <typename HashTableType>
|
| +HashTableModificationForbiddenScope<HashTableType>::~HashTableModificationForbiddenScope()
|
| +{
|
| + leaveScope();
|
| +}
|
| +
|
| +template <typename HashTableType>
|
| +void HashTableModificationForbiddenScope<HashTableType>::leaveScope()
|
| +{
|
| + if (!m_container)
|
| + return;
|
| + m_container->leaveModificationForbiddenScope();
|
| + m_container = nullptr;
|
| +}
|
| +#endif
|
| +
|
| template <WeakHandlingFlag weakHandlingFlag, typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits, typename Allocator>
|
| struct WeakProcessingHashTableHelper;
|
|
|
|
|