Diff
Modified: trunk/Source/WTF/ChangeLog (202001 => 202002)
--- trunk/Source/WTF/ChangeLog 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/ChangeLog 2016-06-13 20:06:22 UTC (rev 202002)
@@ -1,3 +1,64 @@
+2016-06-13 Sam Weinig <[email protected]>
+
+ Make HashMap and HashSet work with Refs
+ https://bugs.webkit.org/show_bug.cgi?id=158638
+
+ Reviewed by Darin Adler.
+
+ * wtf/GetPtr.h:
+ Move HashTableDeletedValueType and HashTableEmptyValueType here, as they are now shared
+ by more than one smart pointer. This file should probably be renamed to something else
+ at some point to indicate that it contains helpers for pointer and ref related functionality.
+
+ * wtf/HashFunctions.h:
+ Add a DefaultHash for Refs. Customize the PtrHash to indicate that it is not safe to compare
+ to empty or deleted Refs.
+
+ * wtf/HashMap.h:
+ (WTF::HashMapTranslator::equal):
+ (WTF::HashMapTranslator::translate):
+ (WTF::HashMapEnsureTranslator::equal):
+ (WTF::HashMapEnsureTranslator::translate):
+ * wtf/HashSet.h:
+ (WTF::IdentityExtractor::extract):
+ (WTF::HashSetTranslator::hash):
+ (WTF::HashSetTranslator::equal):
+ (WTF::HashSetTranslator::translate):
+ * wtf/HashTable.h:
+ (WTF::IdentityHashTranslator::hash):
+ (WTF::IdentityHashTranslator::equal):
+ (WTF::IdentityHashTranslator::translate):
+ Use the new assignToEmpty trait function to allow uninitialized Ref's to be safely assigned to.
+
+ * wtf/HashTraits.h:
+ (WTF::HashTraits<Ref<P>>::emptyValue):
+ (WTF::HashTraits<Ref<P>>::isEmptyValue):
+ (WTF::HashTraits<Ref<P>>::assignToEmpty):
+ (WTF::HashTraits<Ref<P>>::peek):
+ (WTF::HashTraits<Ref<P>>::customDeleteBucket):
+ Add custom HashTraits for Ref. Also, introduce a new trait function, assignToEmpty, for use
+ in translation assignments. This is necessary since the default assignment operator for Ref
+ will not allow assignment to the empty Ref, which we need to do here.
+
+ * wtf/Ref.h:
+ (WTF::Ref::operator=):
+ (WTF::Ref::Ref):
+ (WTF::Ref::isHashTableDeletedValue):
+ (WTF::Ref::hashTableDeletedValue):
+ (WTF::Ref::isHashTableEmptyValue):
+ (WTF::Ref::hashTableEmptyValue):
+ Add explicit constructors/predicates for making deleted/empty Refs.
+
+ (WTF::Ref::assignToHashTableEmptyValue):
+ Add a special function that allows assignment to an empty Ref, which the
+ assignment operator does not.
+
+ (WTF::IsSmartPtr):
+ Add an IsSmartPtr override to indicate that Ref is a smart pointer.
+
+ * wtf/RefPtr.h:
+ Move HashTableDeletedValueType to GetPtr.h.
+
2016-06-13 Fujii Hironori <[email protected]>
Stack overflow at RefPtr::release on Windows port since r201782
Modified: trunk/Source/WTF/wtf/GetPtr.h (202001 => 202002)
--- trunk/Source/WTF/wtf/GetPtr.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/GetPtr.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -25,6 +25,9 @@
namespace WTF {
+enum HashTableDeletedValueType { HashTableDeletedValue };
+enum HashTableEmptyValueType { HashTableEmptyValue };
+
template <typename T> inline T* getPtr(T* p) { return p; }
template <typename T> struct IsSmartPtr {
Modified: trunk/Source/WTF/wtf/HashFunctions.h (202001 => 202002)
--- trunk/Source/WTF/wtf/HashFunctions.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/HashFunctions.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -149,6 +149,10 @@
template<typename T> struct PtrHash : PtrHashBase<T, IsSmartPtr<T>::value> {
};
+ template<typename P> struct PtrHash<Ref<P>> : PtrHashBase<Ref<P>, IsSmartPtr<Ref<P>>::value> {
+ static const bool safeToCompareToEmptyOrDeleted = false;
+ };
+
// default hash function for each type
template<typename T> struct DefaultHash;
@@ -194,6 +198,8 @@
template<typename P> struct DefaultHash<P*> { typedef PtrHash<P*> Hash; };
template<typename P> struct DefaultHash<RefPtr<P>> { typedef PtrHash<RefPtr<P>> Hash; };
+ template<typename P> struct DefaultHash<Ref<P>> { typedef PtrHash<Ref<P>> Hash; };
+
template<typename P, typename Deleter> struct DefaultHash<std::unique_ptr<P, Deleter>> { typedef PtrHash<std::unique_ptr<P, Deleter>> Hash; };
// make IntPairHash the default hash function for pairs of (at most) 32-bit integers.
Modified: trunk/Source/WTF/wtf/HashMap.h (202001 => 202002)
--- trunk/Source/WTF/wtf/HashMap.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/HashMap.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -181,8 +181,8 @@
template<typename T, typename U> static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); }
template<typename T, typename U, typename V> static void translate(T& location, U&& key, V&& mapped)
{
- location.key = std::forward<U>(key);
- location.value = std::forward<V>(mapped);
+ ValueTraits::KeyTraits::assignToEmpty(location.key, std::forward<U>(key));
+ ValueTraits::ValueTraits::assignToEmpty(location.value, std::forward<V>(mapped));
}
};
@@ -192,8 +192,8 @@
template<typename T, typename U> static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); }
template<typename T, typename U, typename Functor> static void translate(T& location, U&& key, const Functor& functor)
{
- location.key = std::forward<U>(key);
- location.value = functor();
+ ValueTraits::KeyTraits::assignToEmpty(location.key, std::forward<U>(key));
+ ValueTraits::ValueTraits::assignToEmpty(location.value, functor());
}
};
Modified: trunk/Source/WTF/wtf/HashSet.h (202001 => 202002)
--- trunk/Source/WTF/wtf/HashSet.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/HashSet.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -128,11 +128,14 @@
template<typename T> static const T& extract(const T& t) { return t; }
};
- template<typename HashFunctions>
+ template<typename ValueTraits, typename HashFunctions>
struct HashSetTranslator {
template<typename T> static unsigned hash(const T& key) { return HashFunctions::hash(key); }
template<typename T, typename U> static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); }
- template<typename T, typename U, typename V> static void translate(T& location, U&&, V&& value) { location = std::forward<V>(value); }
+ template<typename T, typename U, typename V> static void translate(T& location, U&&, V&& value)
+ {
+ ValueTraits::assignToEmpty(location, std::forward<V>(value));
+ }
};
template<typename Translator>
@@ -292,14 +295,14 @@
template<typename V>
inline auto HashSet<Value, HashFunctions, Traits>::find(typename GetPtrHelper<V>::PtrType value) const -> typename std::enable_if<IsSmartPtr<V>::value, iterator>::type
{
- return m_impl.template find<HashSetTranslator<HashFunctions>>(value);
+ return m_impl.template find<HashSetTranslator<Traits, HashFunctions>>(value);
}
template<typename Value, typename HashFunctions, typename Traits>
template<typename V>
inline auto HashSet<Value, HashFunctions, Traits>::contains(typename GetPtrHelper<V>::PtrType value) const -> typename std::enable_if<IsSmartPtr<V>::value, bool>::type
{
- return m_impl.template contains<HashSetTranslator<HashFunctions>>(value);
+ return m_impl.template contains<HashSetTranslator<Traits, HashFunctions>>(value);
}
template<typename Value, typename HashFunctions, typename Traits>
Modified: trunk/Source/WTF/wtf/HashTable.h (202001 => 202002)
--- trunk/Source/WTF/wtf/HashTable.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/HashTable.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -279,11 +279,14 @@
const_iterator m_iterator;
};
- template<typename HashFunctions> class IdentityHashTranslator {
+ template<typename ValueTraits, typename HashFunctions> class IdentityHashTranslator {
public:
template<typename T> static unsigned hash(const T& key) { return HashFunctions::hash(key); }
template<typename T, typename U> static bool equal(const T& a, const U& b) { return HashFunctions::equal(a, b); }
- template<typename T, typename U, typename V> static void translate(T& location, const U&, V&& value) { location = std::forward<V>(value); }
+ template<typename T, typename U, typename V> static void translate(T& location, const U&, V&& value)
+ {
+ ValueTraits::assignToEmpty(location, std::forward<V>(value));
+ }
};
template<typename IteratorType> struct HashTableAddResult {
@@ -303,7 +306,7 @@
typedef Traits ValueTraits;
typedef Key KeyType;
typedef Value ValueType;
- typedef IdentityHashTranslator<HashFunctions> IdentityTranslatorType;
+ typedef IdentityHashTranslator<ValueTraits, HashFunctions> IdentityTranslatorType;
typedef HashTableAddResult<iterator> AddResult;
#if DUMP_HASHTABLE_STATS_PER_TABLE
Modified: trunk/Source/WTF/wtf/HashTraits.h (202001 => 202002)
--- trunk/Source/WTF/wtf/HashTraits.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/HashTraits.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -43,6 +43,12 @@
// for cases like String that need them.
static const bool hasIsEmptyValueFunction = false;
+ template<typename U, typename V>
+ static void assignToEmpty(U& emptyValue, V&& value)
+ {
+ emptyValue = std::forward<V>(value);
+ }
+
// The starting table size. Can be overridden when we know beforehand that
// a hash table will have at least N entries.
static const unsigned minimumTableSize = 8;
@@ -165,6 +171,32 @@
}
};
+template<typename P> struct HashTraits<Ref<P>> : SimpleClassHashTraits<Ref<P>> {
+ static const bool emptyValueIsZero = true;
+ static Ref<P> emptyValue() { return Ref<P>(HashTableEmptyValue); }
+
+ static const bool hasIsEmptyValueFunction = true;
+ static bool isEmptyValue(const Ref<P>& value) { return value.isHashTableEmptyValue(); }
+
+ static void assignToEmpty(Ref<P>& emptyValue, Ref<P>&& newValue)
+ {
+ ASSERT(isEmptyValue(emptyValue));
+ emptyValue.assignToHashTableEmptyValue(WTFMove(newValue));
+ }
+
+ typedef P* PeekType;
+ static PeekType peek(const Ref<P>& value) { return const_cast<PeekType>(value.ptr()); }
+ static PeekType peek(P* value) { return value; }
+
+ static void customDeleteBucket(Ref<P>& value)
+ {
+ // See unique_ptr's customDeleteBucket() for an explanation.
+ ASSERT(!SimpleClassHashTraits<Ref<P>>::isDeletedValue(value));
+ auto valueToBeDestroyed = WTFMove(value);
+ SimpleClassHashTraits<Ref<P>>::constructDeletedValue(value);
+ }
+};
+
template<> struct HashTraits<String> : SimpleClassHashTraits<String> {
static const bool hasIsEmptyValueFunction = true;
static bool isEmptyValue(const String&);
Modified: trunk/Source/WTF/wtf/Ref.h (202001 => 202002)
--- trunk/Source/WTF/wtf/Ref.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/Ref.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -114,6 +114,22 @@
return *this;
}
+ // Hash table deleted/empty values, which are only constructed and never copied or destroyed.
+ Ref(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { }
+ bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); }
+ static T* hashTableDeletedValue() { return reinterpret_cast<T*>(-1); }
+
+ Ref(HashTableEmptyValueType) : m_ptr(hashTableEmptyValue()) { }
+ bool isHashTableEmptyValue() const { return m_ptr == hashTableEmptyValue(); }
+ static T* hashTableEmptyValue() { return nullptr; }
+
+ void assignToHashTableEmptyValue(Ref&& reference)
+ {
+ ASSERT(m_ptr == hashTableEmptyValue());
+ m_ptr = &reference.leakRef();
+ ASSERT(m_ptr);
+ }
+
const T* operator->() const { ASSERT(m_ptr); return m_ptr; }
T* operator->() { ASSERT(m_ptr); return m_ptr; }
@@ -186,12 +202,16 @@
static T* getPtr(const Ref<T>& p) { return const_cast<T*>(p.ptr()); }
};
+template <typename T>
+struct IsSmartPtr<Ref<T>> {
+ static const bool value = true;
+};
+
template<typename T>
inline Ref<T> adoptRef(T& reference)
{
adopted(&reference);
return Ref<T>(reference, Ref<T>::Adopt);
-
}
template<typename ExpectedType, typename ArgType> inline bool is(Ref<ArgType>& source)
Modified: trunk/Source/WTF/wtf/RefPtr.h (202001 => 202002)
--- trunk/Source/WTF/wtf/RefPtr.h 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Source/WTF/wtf/RefPtr.h 2016-06-13 20:06:22 UTC (rev 202002)
@@ -34,8 +34,6 @@
template<typename T> class RefPtr;
template<typename T> RefPtr<T> adoptRef(T*);
-enum HashTableDeletedValueType { HashTableDeletedValue };
-
template<typename T> class RefPtr {
WTF_MAKE_FAST_ALLOCATED;
public:
Modified: trunk/Tools/ChangeLog (202001 => 202002)
--- trunk/Tools/ChangeLog 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Tools/ChangeLog 2016-06-13 20:06:22 UTC (rev 202002)
@@ -1,3 +1,16 @@
+2016-06-13 Sam Weinig <[email protected]>
+
+ Make HashMap and HashSet work with Refs
+ https://bugs.webkit.org/show_bug.cgi?id=158638
+
+ Reviewed by Darin Adler.
+
+ * TestWebKitAPI/Tests/WTF/HashMap.cpp:
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/Tests/WTF/HashSet.cpp:
+ (TestWebKitAPI::TEST):
+ Add tests for using Refs in HashMaps (both as key and value) and HashSets.
+
2016-06-13 Mark Lam <[email protected]>
Add a mechanism for collecting LLINT stats.
Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp (202001 => 202002)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp 2016-06-13 20:06:22 UTC (rev 202002)
@@ -701,4 +701,179 @@
EXPECT_EQ(observer->count, 0u);
}
+TEST(WTF_HashMap, Ref_Key)
+{
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(WTFMove(ref), 1);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.set(WTFMove(ref), 1);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> refA(a);
+ map.add(WTFMove(refA), 1);
+
+ Ref<RefLogger> refA2(a);
+ map.set(WTFMove(refA2), 1);
+ }
+
+ ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.ensure(WTFMove(ref), []() {
+ return 1;
+ });
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(WTFMove(ref), 1);
+
+ auto it = map.find(&a);
+ ASSERT_TRUE(it != map.end());
+
+ ASSERT_EQ(it->key.ptr(), &a);
+ ASSERT_EQ(it->value, 1);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(WTFMove(ref), 1);
+
+ map.remove(&a);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<Ref<RefLogger>, int> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(WTFMove(ref), 1);
+
+ int i = map.take(&a);
+ ASSERT_EQ(i, 1);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+TEST(WTF_HashMap, Ref_Value)
+{
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(1, WTFMove(ref));
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.set(1, WTFMove(ref));
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> refA(a);
+ map.add(1, WTFMove(refA));
+
+ RefLogger b("b");
+ Ref<RefLogger> refB(b);
+ map.set(1, WTFMove(refB));
+ }
+
+ ASSERT_STREQ("ref(a) ref(b) deref(a) deref(b) ", takeLogStr().c_str());
+
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(1, WTFMove(ref));
+
+ ASSERT_EQ(map.get(1), &a);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(1, WTFMove(ref));
+
+ Ref<RefLogger> aOut = map.take(1);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ map.add(1, WTFMove(ref));
+ map.remove(1);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashMap<int, Ref<RefLogger>> map;
+
+ RefLogger a("a");
+ map.ensure(1, [&]() {
+ Ref<RefLogger> ref(a);
+ return ref;
+ });
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
+
+
} // namespace TestWebKitAPI
Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp (202001 => 202002)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp 2016-06-13 19:47:38 UTC (rev 202001)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp 2016-06-13 20:06:22 UTC (rev 202002)
@@ -27,6 +27,7 @@
#include "Counters.h"
#include "MoveOnly.h"
+#include "RefLogger.h"
#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
@@ -348,5 +349,50 @@
EXPECT_TRUE(observedBucket == observerAddress || observedBucket == reinterpret_cast<const DestructorObserver*>(-1));
}
+TEST(WTF_HashSet, Ref)
+{
+ {
+ HashSet<Ref<RefLogger>> set;
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ set.add(WTFMove(ref));
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashSet<Ref<RefLogger>> set;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ set.add(ref.copyRef());
+ }
+
+ ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashSet<Ref<RefLogger>> set;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ set.add(WTFMove(ref));
+ set.remove(&a);
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+
+ {
+ HashSet<Ref<RefLogger>> set;
+
+ RefLogger a("a");
+ Ref<RefLogger> ref(a);
+ set.add(WTFMove(ref));
+
+ ASSERT_TRUE(set.contains(&a));
+ }
+
+ ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
+}
+
} // namespace TestWebKitAPI