1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "mozilla/Maybe.h"
12 #include "js/GCPolicyAPI.h"
13 #include "js/HashTable.h"
14 #include "js/RootingAPI.h"
15 #include "js/TypeDecls.h"
21 // Define a reasonable default GC policy for GC-aware Maps.
22 template <typename Key
, typename Value
>
23 struct DefaultMapEntryGCPolicy
{
24 static bool traceWeak(JSTracer
* trc
, Key
* key
, Value
* value
) {
25 return GCPolicy
<Key
>::traceWeak(trc
, key
) &&
26 GCPolicy
<Value
>::traceWeak(trc
, value
);
30 // A GCHashMap is a GC-aware HashMap, meaning that it has additional trace
31 // methods that know how to visit all keys and values in the table. HashMaps
32 // that contain GC pointers will generally want to use this GCHashMap
33 // specialization instead of HashMap, because this conveniently supports tracing
34 // keys and values, and cleaning up weak entries.
36 // GCHashMap::trace applies GCPolicy<T>::trace to each entry's key and value.
37 // Most types of GC pointers already have appropriate specializations of
38 // GCPolicy, so they should just work as keys and values. Any struct type with a
39 // default constructor and trace function should work as well. If you need to
40 // define your own GCPolicy specialization, generic helpers can be found in
41 // js/public/TracingAPI.h.
43 // The MapEntryGCPolicy template parameter controls how the table drops entries
44 // when edges are weakly held. GCHashMap::traceWeak applies the
45 // MapEntryGCPolicy's traceWeak method to each table entry; if it returns true,
46 // the entry is dropped. The default MapEntryGCPolicy drops the entry if either
47 // the key or value is about to be finalized, according to its
48 // GCPolicy<T>::traceWeak method. (This default is almost always fine: it's hard
49 // to imagine keeping such an entry around anyway.)
51 // Note that this HashMap only knows *how* to trace, but it does not itself
52 // cause tracing to be invoked. For tracing, it must be used as
53 // Rooted<GCHashMap> or PersistentRooted<GCHashMap>, or barriered and traced
55 template <typename Key
, typename Value
,
56 typename HashPolicy
= js::DefaultHasher
<Key
>,
57 typename AllocPolicy
= js::TempAllocPolicy
,
58 typename MapEntryGCPolicy
= DefaultMapEntryGCPolicy
<Key
, Value
>>
59 class GCHashMap
: public js::HashMap
<Key
, Value
, HashPolicy
, AllocPolicy
> {
60 using Base
= js::HashMap
<Key
, Value
, HashPolicy
, AllocPolicy
>;
63 using EntryGCPolicy
= MapEntryGCPolicy
;
65 explicit GCHashMap() : Base(AllocPolicy()) {}
66 explicit GCHashMap(AllocPolicy a
) : Base(std::move(a
)) {}
67 explicit GCHashMap(size_t length
) : Base(length
) {}
68 GCHashMap(AllocPolicy a
, size_t length
) : Base(std::move(a
), length
) {}
70 void trace(JSTracer
* trc
) {
71 for (typename
Base::Enum
e(*this); !e
.empty(); e
.popFront()) {
72 GCPolicy
<Value
>::trace(trc
, &e
.front().value(), "hashmap value");
73 GCPolicy
<Key
>::trace(trc
, &e
.front().mutableKey(), "hashmap key");
77 bool traceWeak(JSTracer
* trc
) {
78 typename
Base::Enum
e(*this);
79 traceWeakEntries(trc
, e
);
80 return !this->empty();
83 void traceWeakEntries(JSTracer
* trc
, typename
Base::Enum
& e
) {
84 for (typename
Base::Enum
e(*this); !e
.empty(); e
.popFront()) {
85 if (!MapEntryGCPolicy::traceWeak(trc
, &e
.front().mutableKey(),
86 &e
.front().value())) {
92 // GCHashMap is movable
93 GCHashMap(GCHashMap
&& rhs
) : Base(std::move(rhs
)) {}
94 void operator=(GCHashMap
&& rhs
) {
95 MOZ_ASSERT(this != &rhs
, "self-move assignment is prohibited");
96 Base::operator=(std::move(rhs
));
100 // GCHashMap is not copyable or assignable
101 GCHashMap(const GCHashMap
& hm
) = delete;
102 GCHashMap
& operator=(const GCHashMap
& hm
) = delete;
103 } MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS
;
109 // HashMap that supports rekeying.
111 // If your keys are pointers to something like JSObject that can be tenured or
112 // compacted, prefer to use GCHashMap with StableCellHasher, which takes
113 // advantage of the Zone's stable id table to make rekeying unnecessary.
114 template <typename Key
, typename Value
,
115 typename HashPolicy
= DefaultHasher
<Key
>,
116 typename AllocPolicy
= TempAllocPolicy
,
117 typename MapEntryGCPolicy
= JS::DefaultMapEntryGCPolicy
<Key
, Value
>>
118 class GCRekeyableHashMap
: public JS::GCHashMap
<Key
, Value
, HashPolicy
,
119 AllocPolicy
, MapEntryGCPolicy
> {
120 using Base
= JS::GCHashMap
<Key
, Value
, HashPolicy
, AllocPolicy
>;
123 explicit GCRekeyableHashMap(AllocPolicy a
= AllocPolicy())
124 : Base(std::move(a
)) {}
125 explicit GCRekeyableHashMap(size_t length
) : Base(length
) {}
126 GCRekeyableHashMap(AllocPolicy a
, size_t length
)
127 : Base(std::move(a
), length
) {}
129 bool traceWeak(JSTracer
* trc
) {
130 for (typename
Base::Enum
e(*this); !e
.empty(); e
.popFront()) {
131 Key
key(e
.front().key());
132 if (!MapEntryGCPolicy::traceWeak(trc
, &key
, &e
.front().value())) {
134 } else if (!HashPolicy::match(key
, e
.front().key())) {
138 return !this->empty();
141 // GCRekeyableHashMap is movable
142 GCRekeyableHashMap(GCRekeyableHashMap
&& rhs
) : Base(std::move(rhs
)) {}
143 void operator=(GCRekeyableHashMap
&& rhs
) {
144 MOZ_ASSERT(this != &rhs
, "self-move assignment is prohibited");
145 Base::operator=(std::move(rhs
));
147 } MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS
;
149 template <typename Wrapper
, typename
... Args
>
150 class WrappedPtrOperations
<JS::GCHashMap
<Args
...>, Wrapper
> {
151 using Map
= JS::GCHashMap
<Args
...>;
152 using Lookup
= typename
Map::Lookup
;
154 const Map
& map() const { return static_cast<const Wrapper
*>(this)->get(); }
157 using AddPtr
= typename
Map::AddPtr
;
158 using Ptr
= typename
Map::Ptr
;
159 using Range
= typename
Map::Range
;
161 Ptr
lookup(const Lookup
& l
) const { return map().lookup(l
); }
162 Range
all() const { return map().all(); }
163 bool empty() const { return map().empty(); }
164 uint32_t count() const { return map().count(); }
165 size_t capacity() const { return map().capacity(); }
166 bool has(const Lookup
& l
) const { return map().lookup(l
).found(); }
167 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const {
168 return map().sizeOfExcludingThis(mallocSizeOf
);
170 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const {
171 return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf
);
175 template <typename Wrapper
, typename
... Args
>
176 class MutableWrappedPtrOperations
<JS::GCHashMap
<Args
...>, Wrapper
>
177 : public WrappedPtrOperations
<JS::GCHashMap
<Args
...>, Wrapper
> {
178 using Map
= JS::GCHashMap
<Args
...>;
179 using Lookup
= typename
Map::Lookup
;
181 Map
& map() { return static_cast<Wrapper
*>(this)->get(); }
184 using AddPtr
= typename
Map::AddPtr
;
185 struct Enum
: public Map::Enum
{
186 explicit Enum(Wrapper
& o
) : Map::Enum(o
.map()) {}
188 using Ptr
= typename
Map::Ptr
;
189 using Range
= typename
Map::Range
;
191 void clear() { map().clear(); }
192 void clearAndCompact() { map().clearAndCompact(); }
193 void remove(Ptr p
) { map().remove(p
); }
194 AddPtr
lookupForAdd(const Lookup
& l
) { return map().lookupForAdd(l
); }
196 template <typename KeyInput
, typename ValueInput
>
197 bool add(AddPtr
& p
, KeyInput
&& k
, ValueInput
&& v
) {
198 return map().add(p
, std::forward
<KeyInput
>(k
), std::forward
<ValueInput
>(v
));
201 template <typename KeyInput
>
202 bool add(AddPtr
& p
, KeyInput
&& k
) {
203 return map().add(p
, std::forward
<KeyInput
>(k
), Map::Value());
206 template <typename KeyInput
, typename ValueInput
>
207 bool relookupOrAdd(AddPtr
& p
, KeyInput
&& k
, ValueInput
&& v
) {
208 return map().relookupOrAdd(p
, k
, std::forward
<KeyInput
>(k
),
209 std::forward
<ValueInput
>(v
));
212 template <typename KeyInput
, typename ValueInput
>
213 bool put(KeyInput
&& k
, ValueInput
&& v
) {
214 return map().put(std::forward
<KeyInput
>(k
), std::forward
<ValueInput
>(v
));
217 template <typename KeyInput
, typename ValueInput
>
218 bool putNew(KeyInput
&& k
, ValueInput
&& v
) {
219 return map().putNew(std::forward
<KeyInput
>(k
), std::forward
<ValueInput
>(v
));
227 // A GCHashSet is a HashSet with an additional trace method that knows
228 // be traced to be kept alive will generally want to use this GCHashSet
229 // specialization in lieu of HashSet.
231 // Most types of GC pointers can be traced with no extra infrastructure. For
232 // structs and non-gc-pointer members, ensure that there is a specialization of
233 // GCPolicy<T> with an appropriate trace method available to handle the custom
234 // type. Generic helpers can be found in js/public/TracingAPI.h.
236 // Note that although this HashSet's trace will deal correctly with moved
237 // elements, it does not itself know when to barrier or trace elements. To
238 // function properly it must either be used with Rooted or barriered and traced
240 template <typename T
, typename HashPolicy
= js::DefaultHasher
<T
>,
241 typename AllocPolicy
= js::TempAllocPolicy
>
242 class GCHashSet
: public js::HashSet
<T
, HashPolicy
, AllocPolicy
> {
243 using Base
= js::HashSet
<T
, HashPolicy
, AllocPolicy
>;
246 explicit GCHashSet(AllocPolicy a
= AllocPolicy()) : Base(std::move(a
)) {}
247 explicit GCHashSet(size_t length
) : Base(length
) {}
248 GCHashSet(AllocPolicy a
, size_t length
) : Base(std::move(a
), length
) {}
250 void trace(JSTracer
* trc
) {
251 for (typename
Base::Enum
e(*this); !e
.empty(); e
.popFront()) {
252 GCPolicy
<T
>::trace(trc
, &e
.mutableFront(), "hashset element");
256 bool traceWeak(JSTracer
* trc
) {
257 typename
Base::Enum
e(*this);
258 traceWeakEntries(trc
, e
);
259 return !this->empty();
262 void traceWeakEntries(JSTracer
* trc
, typename
Base::Enum
& e
) {
263 for (; !e
.empty(); e
.popFront()) {
264 if (!GCPolicy
<T
>::traceWeak(trc
, &e
.mutableFront())) {
270 // GCHashSet is movable
271 GCHashSet(GCHashSet
&& rhs
) : Base(std::move(rhs
)) {}
272 void operator=(GCHashSet
&& rhs
) {
273 MOZ_ASSERT(this != &rhs
, "self-move assignment is prohibited");
274 Base::operator=(std::move(rhs
));
278 // GCHashSet is not copyable or assignable
279 GCHashSet(const GCHashSet
& hs
) = delete;
280 GCHashSet
& operator=(const GCHashSet
& hs
) = delete;
281 } MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS
;
287 template <typename Wrapper
, typename
... Args
>
288 class WrappedPtrOperations
<JS::GCHashSet
<Args
...>, Wrapper
> {
289 using Set
= JS::GCHashSet
<Args
...>;
291 const Set
& set() const { return static_cast<const Wrapper
*>(this)->get(); }
294 using Lookup
= typename
Set::Lookup
;
295 using AddPtr
= typename
Set::AddPtr
;
296 using Entry
= typename
Set::Entry
;
297 using Ptr
= typename
Set::Ptr
;
298 using Range
= typename
Set::Range
;
300 Ptr
lookup(const Lookup
& l
) const { return set().lookup(l
); }
301 Range
all() const { return set().all(); }
302 bool empty() const { return set().empty(); }
303 uint32_t count() const { return set().count(); }
304 size_t capacity() const { return set().capacity(); }
305 bool has(const Lookup
& l
) const { return set().lookup(l
).found(); }
306 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const {
307 return set().sizeOfExcludingThis(mallocSizeOf
);
309 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const {
310 return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf
);
314 template <typename Wrapper
, typename
... Args
>
315 class MutableWrappedPtrOperations
<JS::GCHashSet
<Args
...>, Wrapper
>
316 : public WrappedPtrOperations
<JS::GCHashSet
<Args
...>, Wrapper
> {
317 using Set
= JS::GCHashSet
<Args
...>;
318 using Lookup
= typename
Set::Lookup
;
320 Set
& set() { return static_cast<Wrapper
*>(this)->get(); }
323 using AddPtr
= typename
Set::AddPtr
;
324 using Entry
= typename
Set::Entry
;
325 struct Enum
: public Set::Enum
{
326 explicit Enum(Wrapper
& o
) : Set::Enum(o
.set()) {}
328 using Ptr
= typename
Set::Ptr
;
329 using Range
= typename
Set::Range
;
331 void clear() { set().clear(); }
332 void clearAndCompact() { set().clearAndCompact(); }
333 [[nodiscard
]] bool reserve(uint32_t len
) { return set().reserve(len
); }
334 void remove(Ptr p
) { set().remove(p
); }
335 void remove(const Lookup
& l
) { set().remove(l
); }
336 AddPtr
lookupForAdd(const Lookup
& l
) { return set().lookupForAdd(l
); }
338 template <typename TInput
>
339 void replaceKey(Ptr p
, const Lookup
& l
, TInput
&& newValue
) {
340 set().replaceKey(p
, l
, std::forward
<TInput
>(newValue
));
343 template <typename TInput
>
344 bool add(AddPtr
& p
, TInput
&& t
) {
345 return set().add(p
, std::forward
<TInput
>(t
));
348 template <typename TInput
>
349 bool relookupOrAdd(AddPtr
& p
, const Lookup
& l
, TInput
&& t
) {
350 return set().relookupOrAdd(p
, l
, std::forward
<TInput
>(t
));
353 template <typename TInput
>
354 bool put(TInput
&& t
) {
355 return set().put(std::forward
<TInput
>(t
));
358 template <typename TInput
>
359 bool putNew(TInput
&& t
) {
360 return set().putNew(std::forward
<TInput
>(t
));
363 template <typename TInput
>
364 bool putNew(const Lookup
& l
, TInput
&& t
) {
365 return set().putNew(l
, std::forward
<TInput
>(t
));
371 #endif /* GCHashTable_h */