Commit 09a7591a authored by Kei Nakashima's avatar Kei Nakashima Committed by Commit Bot

Added a class |HeapNewLinkedHashSet|

Implemented |HeapNewLinkedHashSet|, which inherits from NewLinkedHashSet.
Along with this change, added allocator to template parameter of NewLinkedHashSet and VectorBackedLinkedList.
Also added a test for it.

Change-Id: I277b8542924d52c1fa4eb7815f2feac0074ed4c4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2105667
Commit-Queue: Kei Nakashima <keinakashima@google.com>
Reviewed-by: default avatarBartek Nowierski <bartekn@chromium.org>
Reviewed-by: default avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarKeishi Hattori <keishi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751638}
parent 7792d341
...@@ -220,6 +220,37 @@ TEST_F(ConcurrentMarkingTest, SwapLinkedHashSet) { ...@@ -220,6 +220,37 @@ TEST_F(ConcurrentMarkingTest, SwapLinkedHashSet) {
SwapCollections<HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); SwapCollections<HeapLinkedHashSetAdapter<Member<IntegerObject>>>();
} }
// HeapNewLinkedHashSet
template <typename T>
class HeapNewLinkedHashSetAdapter : public HeapNewLinkedHashSet<T> {
public:
ALWAYS_INLINE void swap(HeapNewLinkedHashSetAdapter<T>& other) {
HeapNewLinkedHashSet<T>::Swap(other);
}
};
TEST_F(ConcurrentMarkingTest, AddToNewLinkedHashSet) {
AddToCollection<HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>();
}
TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfNewLinkedHashSet) {
RemoveFromBeginningOfCollection<
HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>();
}
TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfNewLinkedHashSet) {
RemoveFromMiddleOfCollection<
HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>();
}
TEST_F(ConcurrentMarkingTest, RemoveFromEndOfNewLinkedHashSet) {
RemoveFromEndOfCollection<
HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>();
}
TEST_F(ConcurrentMarkingTest, ClearNewLinkedHashSet) {
ClearCollection<HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>();
}
TEST_F(ConcurrentMarkingTest, SwapNewLinkedHashSet) {
SwapCollections<HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>();
}
// HeapListHashSet // HeapListHashSet
template <typename T> template <typename T>
......
...@@ -511,6 +511,41 @@ template <typename T, typename U, typename V> ...@@ -511,6 +511,41 @@ template <typename T, typename U, typename V>
struct GCInfoTrait<HeapLinkedHashSet<T, U, V>> struct GCInfoTrait<HeapLinkedHashSet<T, U, V>>
: public GCInfoTrait<LinkedHashSet<T, U, V, HeapAllocator>> {}; : public GCInfoTrait<LinkedHashSet<T, U, V, HeapAllocator>> {};
// This class is still experimental. Do not use this class.
template <typename ValueArg>
class HeapNewLinkedHashSet : public NewLinkedHashSet<ValueArg, HeapAllocator> {
IS_GARBAGE_COLLECTED_CONTAINER_TYPE();
DISALLOW_NEW();
static void CheckType() {
// TODO(keinakashima): support WeakMember<T>
static_assert(internal::IsMember<ValueArg>,
"HeapNewLinkedHashSet supports only Member.");
// If not trivially destructible, we have to add a destructor which will
// hinder performance.
static_assert(std::is_trivially_destructible<HeapNewLinkedHashSet>::value,
"HeapNewLinkedHashSet must be trivially destructible.");
static_assert(
IsAllowedInContainer<ValueArg>::value,
"Not allowed to directly nest type. Use Member<> indirection instead.");
static_assert(WTF::IsTraceable<ValueArg>::value,
"For sets without traceable elements, use NewLinkedHashSet<> "
"instead of HeapNewLinkedHashSet<>.");
}
public:
template <typename>
static void* AllocateObject(size_t size) {
return ThreadHeap::Allocate<HeapNewLinkedHashSet<ValueArg>>(size);
}
HeapNewLinkedHashSet() { CheckType(); }
};
template <typename T>
struct GCInfoTrait<HeapNewLinkedHashSet<T>>
: public GCInfoTrait<NewLinkedHashSet<T, HeapAllocator>> {};
template <typename ValueArg, template <typename ValueArg,
wtf_size_t inlineCapacity = wtf_size_t inlineCapacity =
0, // The inlineCapacity is just a dummy to 0, // The inlineCapacity is just a dummy to
......
...@@ -337,6 +337,135 @@ TEST_F(HeapCompactTest, CompactLinkedHashSetNested) { ...@@ -337,6 +337,135 @@ TEST_F(HeapCompactTest, CompactLinkedHashSetNested) {
} }
} }
TEST_F(HeapCompactTest, CompactNewLinkedHashSet) {
using OrderedHashSet = HeapNewLinkedHashSet<Member<IntWrapper>>;
Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
for (int i = 0; i < 13; ++i) {
IntWrapper* value = IntWrapper::Create(i, HashTablesAreCompacted);
set->insert(value);
}
EXPECT_EQ(13u, set->size());
int expected = 0;
for (IntWrapper* v : *set) {
EXPECT_EQ(expected, v->Value());
expected++;
}
for (int i = 1; i < 13; i += 2) {
auto it = set->begin();
for (int j = 0; j < (i + 1) / 2; ++j) {
++it;
}
set->erase(it);
}
EXPECT_EQ(7u, set->size());
expected = 0;
for (IntWrapper* v : *set) {
EXPECT_EQ(expected, v->Value());
expected += 2;
}
PerformHeapCompaction();
EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
expected = 0;
for (IntWrapper* v : *set) {
EXPECT_EQ(expected, v->Value());
expected += 2;
}
EXPECT_EQ(7u, set->size());
}
TEST_F(HeapCompactTest, CompactNewLinkedHashSetVector) {
using OrderedHashSet = HeapNewLinkedHashSet<Member<IntVector>>;
Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
for (int i = 0; i < 13; ++i) {
IntWrapper* value = IntWrapper::Create(i);
IntVector* vector = MakeGarbageCollected<IntVector>(19, value);
set->insert(vector);
}
EXPECT_EQ(13u, set->size());
int expected = 0;
for (IntVector* v : *set) {
EXPECT_EQ(expected, (*v)[0]->Value());
expected++;
}
PerformHeapCompaction();
EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
expected = 0;
for (IntVector* v : *set) {
EXPECT_EQ(expected, (*v)[0]->Value());
expected++;
}
}
TEST_F(HeapCompactTest, CompactNewLinkedHashSetMap) {
using Inner = HeapHashSet<Member<IntWrapper>>;
using OrderedHashSet = HeapNewLinkedHashSet<Member<Inner>>;
Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
for (int i = 0; i < 13; ++i) {
IntWrapper* value = IntWrapper::Create(i);
Inner* inner = MakeGarbageCollected<Inner>();
inner->insert(value);
set->insert(inner);
}
EXPECT_EQ(13u, set->size());
int expected = 0;
for (const Inner* v : *set) {
EXPECT_EQ(1u, v->size());
EXPECT_EQ(expected, (*v->begin())->Value());
expected++;
}
PerformHeapCompaction();
EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
expected = 0;
for (const Inner* v : *set) {
EXPECT_EQ(1u, v->size());
EXPECT_EQ(expected, (*v->begin())->Value());
expected++;
}
}
TEST_F(HeapCompactTest, CompactNewLinkedHashSetNested) {
using Inner = HeapNewLinkedHashSet<Member<IntWrapper>>;
using OrderedHashSet = HeapNewLinkedHashSet<Member<Inner>>;
Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>();
for (int i = 0; i < 13; ++i) {
IntWrapper* value = IntWrapper::Create(i);
Inner* inner = MakeGarbageCollected<Inner>();
inner->insert(value);
set->insert(inner);
}
EXPECT_EQ(13u, set->size());
int expected = 0;
for (const Inner* v : *set) {
EXPECT_EQ(1u, v->size());
EXPECT_EQ(expected, (*v->begin())->Value());
expected++;
}
PerformHeapCompaction();
EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
expected = 0;
for (const Inner* v : *set) {
EXPECT_EQ(1u, v->size());
EXPECT_EQ(expected, (*v->begin())->Value());
expected++;
}
}
TEST_F(HeapCompactTest, CompactInlinedBackingStore) { TEST_F(HeapCompactTest, CompactInlinedBackingStore) {
// Regression test: https://crbug.com/875044 // Regression test: https://crbug.com/875044
// //
......
...@@ -3128,6 +3128,9 @@ TEST_F(HeapTest, HeapWeakLinkedHashSet) { ...@@ -3128,6 +3128,9 @@ TEST_F(HeapTest, HeapWeakLinkedHashSet) {
OrderedSetHelper<HeapLinkedHashSet<WeakMember<IntWrapper>>>(false); OrderedSetHelper<HeapLinkedHashSet<WeakMember<IntWrapper>>>(false);
ClearOutOldGarbage(); ClearOutOldGarbage();
OrderedSetHelper<HeapListHashSet<Member<IntWrapper>>>(true); OrderedSetHelper<HeapListHashSet<Member<IntWrapper>>>(true);
ClearOutOldGarbage();
// TODO(keinakashiima): add a test case for WeakMember once it's supported
OrderedSetHelper<HeapNewLinkedHashSet<Member<IntWrapper>>>(true);
} }
class ThingWithDestructor { class ThingWithDestructor {
...@@ -5223,6 +5226,9 @@ TEST_F(HeapTest, IsGarbageCollected) { ...@@ -5223,6 +5226,9 @@ TEST_F(HeapTest, IsGarbageCollected) {
static_assert( static_assert(
WTF::IsGarbageCollectedType<HeapLinkedHashSet<Member<IntWrapper>>>::value, WTF::IsGarbageCollectedType<HeapLinkedHashSet<Member<IntWrapper>>>::value,
"HeapLinkedHashSet"); "HeapLinkedHashSet");
static_assert(WTF::IsGarbageCollectedType<
HeapNewLinkedHashSet<Member<IntWrapper>>>::value,
"HeapNewLinkedHashSet");
static_assert( static_assert(
WTF::IsGarbageCollectedType<HeapListHashSet<Member<IntWrapper>>>::value, WTF::IsGarbageCollectedType<HeapListHashSet<Member<IntWrapper>>>::value,
"HeapListHashSet"); "HeapListHashSet");
......
...@@ -961,6 +961,9 @@ TEST_F(IncrementalMarkingTest, HeapLinkedHashSetSwap) { ...@@ -961,6 +961,9 @@ TEST_F(IncrementalMarkingTest, HeapLinkedHashSetSwap) {
Swap<HeapLinkedHashSet<WeakMember<Object>>>(); Swap<HeapLinkedHashSet<WeakMember<Object>>>();
} }
// TODO(keinakashima): add tests for NewLinkedHashSet after supporting
// WeakMember
// ============================================================================= // =============================================================================
// HeapHashCountedSet support. ================================================= // HeapHashCountedSet support. =================================================
// ============================================================================= // =============================================================================
......
...@@ -198,6 +198,8 @@ TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) { ...@@ -198,6 +198,8 @@ TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) {
EXPECT_NE(old_ephemeron_count, ephemeron_count); EXPECT_NE(old_ephemeron_count, ephemeron_count);
} }
// TODO(keinakashima): add tests for NewLinkedHashSet after supporting
// WeakMember
TEST_F(WeaknessMarkingTest, SwapIntoAlreadyProcessedWeakSet) { TEST_F(WeaknessMarkingTest, SwapIntoAlreadyProcessedWeakSet) {
// Regression test: https://crbug.com/1038623 // Regression test: https://crbug.com/1038623
// //
......
...@@ -1029,7 +1029,7 @@ inline void swap(LinkedHashSetNode<T>& a, LinkedHashSetNode<T>& b) { ...@@ -1029,7 +1029,7 @@ inline void swap(LinkedHashSetNode<T>& a, LinkedHashSetNode<T>& b) {
// TODO(keinakashima): replace existing LinkedHashSet with NewLinkedHashSet // TODO(keinakashima): replace existing LinkedHashSet with NewLinkedHashSet
// after completion // after completion
// This class is yet experimental. Do not use this class. // This class is still experimental. Do not use this class.
// LinkedHashSet provides a Set interface like HashSet, but also has a // LinkedHashSet provides a Set interface like HashSet, but also has a
// predictable iteration order. It has O(1) insertion, removal, and test for // predictable iteration order. It has O(1) insertion, removal, and test for
...@@ -1041,18 +1041,25 @@ inline void swap(LinkedHashSetNode<T>& a, LinkedHashSetNode<T>& b) { ...@@ -1041,18 +1041,25 @@ inline void swap(LinkedHashSetNode<T>& a, LinkedHashSetNode<T>& b) {
// TODO(keinakashima): implement NewLinkedHashTraits (now we cannot insert // TODO(keinakashima): implement NewLinkedHashTraits (now we cannot insert
// deleted/empty value) and add it to template parameter // deleted/empty value) and add it to template parameter
template <typename ValueArg> template <typename ValueArg, typename Allocator = PartitionAllocator>
class NewLinkedHashSet { class NewLinkedHashSet {
USING_FAST_MALLOC(NewLinkedHashSet); USE_ALLOCATOR(NewLinkedHashSet, Allocator);
private: private:
using Value = ValueArg; using Value = ValueArg;
using Map = HashMap<Value, wtf_size_t>; using Map = HashMap<Value,
wtf_size_t,
typename DefaultHash<Value>::Hash,
HashTraits<Value>,
HashTraits<wtf_size_t>,
Allocator>;
using ListType = VectorBackedLinkedList<Value, Allocator>;
public: public:
using const_iterator = typename VectorBackedLinkedList<Value>::const_iterator; using iterator = typename ListType::const_iterator;
using const_reverse_iterator = using reverse_iterator = typename ListType::const_reverse_iterator;
typename VectorBackedLinkedList<Value>::const_reverse_iterator; using const_iterator = typename ListType::const_iterator;
using const_reverse_iterator = typename ListType::const_reverse_iterator;
// TODO(keinakashima): add security check // TODO(keinakashima): add security check
struct AddResult final { struct AddResult final {
...@@ -1117,7 +1124,11 @@ class NewLinkedHashSet { ...@@ -1117,7 +1124,11 @@ class NewLinkedHashSet {
list_.clear(); list_.clear();
} }
// TODO(keinakashima): implement Trace template <typename VisitorDispatcher, typename A = Allocator>
std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) {
value_to_index_.Trace(visitor);
list_.Trace(visitor);
}
private: private:
enum class MoveType { enum class MoveType {
...@@ -1128,47 +1139,52 @@ class NewLinkedHashSet { ...@@ -1128,47 +1139,52 @@ class NewLinkedHashSet {
template <typename IncomingValueType> template <typename IncomingValueType>
AddResult InsertOrMoveBefore(const_iterator, IncomingValueType&&, MoveType); AddResult InsertOrMoveBefore(const_iterator, IncomingValueType&&, MoveType);
HashMap<Value, wtf_size_t> value_to_index_; Map value_to_index_;
VectorBackedLinkedList<Value> list_; ListType list_;
}; };
template <typename T> template <typename T, typename Allocator>
NewLinkedHashSet<T>::NewLinkedHashSet() { NewLinkedHashSet<T, Allocator>::NewLinkedHashSet() {
// TODO(keinakashima): add assertion when considering GC static_assert(Allocator::kIsGarbageCollected ||
!IsPointerToGarbageCollectedType<T>::value,
"Cannot put raw pointers to garbage-collected classes into "
"an off-heap NewLinkedHashSet. Use "
"HeapNewLinkedHashSet<Member<T>> instead.");
} }
// TODO(keinakashima): add copy constructor after implementing iterator if // TODO(keinakashima): add copy constructor after implementing iterator if
// anybody uses it. // anybody uses it.
template <typename T> template <typename T, typename Allocator>
inline NewLinkedHashSet<T>::NewLinkedHashSet(NewLinkedHashSet&& other) { inline NewLinkedHashSet<T, Allocator>::NewLinkedHashSet(
NewLinkedHashSet&& other) {
Swap(other); Swap(other);
} }
template <typename T> template <typename T, typename Allocator>
inline NewLinkedHashSet<T>& NewLinkedHashSet<T>::operator=( inline NewLinkedHashSet<T, Allocator>& NewLinkedHashSet<T, Allocator>::
const NewLinkedHashSet& other) { operator=(const NewLinkedHashSet& other) {
NewLinkedHashSet tmp(other); NewLinkedHashSet tmp(other);
Swap(tmp); Swap(tmp);
return *this; return *this;
} }
template <typename T> template <typename T, typename Allocator>
inline NewLinkedHashSet<T>& NewLinkedHashSet<T>::operator=( inline NewLinkedHashSet<T, Allocator>& NewLinkedHashSet<T, Allocator>::
NewLinkedHashSet&& other) { operator=(NewLinkedHashSet&& other) {
Swap(other); Swap(other);
return *this; return *this;
} }
template <typename T> template <typename T, typename Allocator>
inline void NewLinkedHashSet<T>::Swap(NewLinkedHashSet& other) { inline void NewLinkedHashSet<T, Allocator>::Swap(NewLinkedHashSet& other) {
value_to_index_.swap(other.value_to_index_); value_to_index_.swap(other.value_to_index_);
list_.swap(other.list_); list_.swap(other.list_);
} }
template <typename T> template <typename T, typename Allocator>
typename NewLinkedHashSet<T>::const_iterator NewLinkedHashSet<T>::find( typename NewLinkedHashSet<T, Allocator>::const_iterator
ValuePeekInType value) const { NewLinkedHashSet<T, Allocator>::find(ValuePeekInType value) const {
typename Map::const_iterator it = value_to_index_.find(value); typename Map::const_iterator it = value_to_index_.find(value);
if (it == value_to_index_.end()) if (it == value_to_index_.end())
...@@ -1176,76 +1192,77 @@ typename NewLinkedHashSet<T>::const_iterator NewLinkedHashSet<T>::find( ...@@ -1176,76 +1192,77 @@ typename NewLinkedHashSet<T>::const_iterator NewLinkedHashSet<T>::find(
return list_.MakeConstIterator(it->value); return list_.MakeConstIterator(it->value);
} }
template <typename T> template <typename T, typename Allocator>
bool NewLinkedHashSet<T>::Contains(ValuePeekInType value) const { bool NewLinkedHashSet<T, Allocator>::Contains(ValuePeekInType value) const {
return value_to_index_.Contains(value); return value_to_index_.Contains(value);
} }
template <typename T> template <typename T, typename Allocator>
template <typename IncomingValueType> template <typename IncomingValueType>
typename NewLinkedHashSet<T>::AddResult NewLinkedHashSet<T>::insert( typename NewLinkedHashSet<T, Allocator>::AddResult
IncomingValueType&& value) { NewLinkedHashSet<T, Allocator>::insert(IncomingValueType&& value) {
return InsertOrMoveBefore(end(), std::forward<IncomingValueType>(value), return InsertOrMoveBefore(end(), std::forward<IncomingValueType>(value),
MoveType::kDontMove); MoveType::kDontMove);
} }
template <typename T> template <typename T, typename Allocator>
template <typename IncomingValueType> template <typename IncomingValueType>
typename NewLinkedHashSet<T>::AddResult NewLinkedHashSet<T>::InsertBefore( typename NewLinkedHashSet<T, Allocator>::AddResult
ValuePeekInType before_value, NewLinkedHashSet<T, Allocator>::InsertBefore(ValuePeekInType before_value,
IncomingValueType&& value) { IncomingValueType&& value) {
return InsertOrMoveBefore(find(before_value), return InsertOrMoveBefore(find(before_value),
std::forward<IncomingValueType>(value), std::forward<IncomingValueType>(value),
MoveType::kDontMove); MoveType::kDontMove);
} }
template <typename T> template <typename T, typename Allocator>
template <typename IncomingValueType> template <typename IncomingValueType>
typename NewLinkedHashSet<T>::AddResult NewLinkedHashSet<T>::AppendOrMoveToLast( typename NewLinkedHashSet<T, Allocator>::AddResult
IncomingValueType&& value) { NewLinkedHashSet<T, Allocator>::AppendOrMoveToLast(IncomingValueType&& value) {
return InsertOrMoveBefore(end(), std::forward<IncomingValueType>(value), return InsertOrMoveBefore(end(), std::forward<IncomingValueType>(value),
MoveType::kMoveIfValueExists); MoveType::kMoveIfValueExists);
} }
template <typename T> template <typename T, typename Allocator>
template <typename IncomingValueType> template <typename IncomingValueType>
typename NewLinkedHashSet<T>::AddResult typename NewLinkedHashSet<T, Allocator>::AddResult
NewLinkedHashSet<T>::PrependOrMoveToFirst(IncomingValueType&& value) { NewLinkedHashSet<T, Allocator>::PrependOrMoveToFirst(
IncomingValueType&& value) {
return InsertOrMoveBefore(begin(), std::forward<IncomingValueType>(value), return InsertOrMoveBefore(begin(), std::forward<IncomingValueType>(value),
MoveType::kMoveIfValueExists); MoveType::kMoveIfValueExists);
} }
template <typename T> template <typename T, typename Allocator>
inline void NewLinkedHashSet<T>::erase(ValuePeekInType value) { inline void NewLinkedHashSet<T, Allocator>::erase(ValuePeekInType value) {
erase(find(value)); erase(find(value));
} }
template <typename T> template <typename T, typename Allocator>
inline void NewLinkedHashSet<T>::erase(const_iterator it) { inline void NewLinkedHashSet<T, Allocator>::erase(const_iterator it) {
if (it == end()) if (it == end())
return; return;
value_to_index_.erase(*it); value_to_index_.erase(*it);
list_.erase(it); list_.erase(it);
} }
template <typename T> template <typename T, typename Allocator>
inline void NewLinkedHashSet<T>::RemoveFirst() { inline void NewLinkedHashSet<T, Allocator>::RemoveFirst() {
DCHECK(!IsEmpty()); DCHECK(!IsEmpty());
erase(begin()); erase(begin());
} }
template <typename T> template <typename T, typename Allocator>
inline void NewLinkedHashSet<T>::pop_back() { inline void NewLinkedHashSet<T, Allocator>::pop_back() {
DCHECK(!IsEmpty()); DCHECK(!IsEmpty());
erase(--end()); erase(--end());
} }
template <typename T> template <typename T, typename Allocator>
template <typename IncomingValueType> template <typename IncomingValueType>
typename NewLinkedHashSet<T>::AddResult NewLinkedHashSet<T>::InsertOrMoveBefore( typename NewLinkedHashSet<T, Allocator>::AddResult
const_iterator position, NewLinkedHashSet<T, Allocator>::InsertOrMoveBefore(const_iterator position,
IncomingValueType&& value, IncomingValueType&& value,
MoveType type) { MoveType type) {
typename Map::AddResult result = value_to_index_.insert(value, kNotFound); typename Map::AddResult result = value_to_index_.insert(value, kNotFound);
if (result.is_new_entry) { if (result.is_new_entry) {
...@@ -1268,5 +1285,6 @@ typename NewLinkedHashSet<T>::AddResult NewLinkedHashSet<T>::InsertOrMoveBefore( ...@@ -1268,5 +1285,6 @@ typename NewLinkedHashSet<T>::AddResult NewLinkedHashSet<T>::InsertOrMoveBefore(
} // namespace WTF } // namespace WTF
using WTF::LinkedHashSet; using WTF::LinkedHashSet;
using WTF::NewLinkedHashSet;
#endif /* WTF_LinkedHashSet_h */ #endif /* WTF_LinkedHashSet_h */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment