1616namespace js {
1717
1818namespace detail {
19+ // A wrapper for a bare pointer, with no barriers. The only user should be for
20+ // NurseryAwareHashMap; it is defined externally because we need a GCPolicy for
21+ // its use in the contained map.
22+ template <typename T>
23+ class UnsafeBarePtr : public BarrieredBase <T> {
24+ public:
25+ UnsafeBarePtr () : BarrieredBase<T>(JS::SafelyInitialized<T>()) {}
26+ MOZ_IMPLICIT UnsafeBarePtr (T v) : BarrieredBase<T>(v) {}
27+ const T& get () const { return this ->value ; }
28+ void set (T newValue) { this ->value = newValue; }
29+ DECLARE_POINTER_CONSTREF_OPS (T);
30+ };
31+
32+ template <class T >
33+ struct UnsafeBarePtrHasher {
34+ using Key = UnsafeBarePtr<T>;
35+ using Lookup = T;
36+
37+ static HashNumber hash (const Lookup& l) { return DefaultHasher<T>::hash (l); }
38+ static bool match (const Key& k, Lookup l) { return k.get () == l; }
39+ static void rekey (Key& k, const Key& newKey) { k.set (newKey.get ()); }
40+ };
41+
1942// This class only handles the incremental case and does not deal with nursery
2043// pointers. The only users should be for NurseryAwareHashMap; it is defined
2144// externally because we need a GCPolicy for its use in the contained map.
@@ -63,24 +86,23 @@ enum : bool { DuplicatesNotPossible, DuplicatesPossible };
6386// hash table treats such edges strongly.
6487//
6588// Doing this requires some strong constraints on what can be stored in this
66- // table and how it can be accessed. At the moment, this table assumes that
67- // all values contain a strong reference to the key. It also requires the
68- // policy to contain an |isTenured| and |needsSweep| members, which is fairly
69- // non-standard. This limits its usefulness to the CrossCompartmentMap at the
70- // moment, but might serve as a useful base for other tables in future.
89+ // table and how it can be accessed. At the moment, this table assumes that all
90+ // values contain a strong reference to the key. This limits its usefulness to
91+ // the CrossCompartmentMap at the moment, but might serve as a useful base for
92+ // other tables in future.
7193template <typename Key, typename Value,
72- typename HashPolicy = DefaultHasher<Key>,
7394 typename AllocPolicy = TempAllocPolicy,
7495 bool AllowDuplicates = DuplicatesNotPossible>
7596class NurseryAwareHashMap {
76- using BarrieredValue = detail::UnsafeBareWeakHeapPtr<Value>;
77- using MapType =
78- GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
97+ using MapKey = detail::UnsafeBarePtr<Key>;
98+ using MapValue = detail::UnsafeBareWeakHeapPtr<Value>;
99+ using HashPolicy = DefaultHasher<MapKey>;
100+ using MapType = GCRekeyableHashMap<MapKey, MapValue, HashPolicy, AllocPolicy>;
79101 MapType map;
80102
81- // Keep a list of all keys for which JS::GCPolicy<Key>:: isTenured is false.
82- // This lets us avoid a full traveral of the map on each minor GC, keeping
83- // the minor GC times proportional to the nursery heap size.
103+ // Keep a list of all keys for which key-> isTenured() is false. This lets us
104+ // avoid a full traversal of the map on each minor GC, keeping the minor GC
105+ // times proportional to the nursery heap size.
84106 Vector<Key, 0 , AllocPolicy> nurseryEntries;
85107
86108 public:
@@ -111,33 +133,19 @@ class NurseryAwareHashMap {
111133 nurseryEntries.sizeOfIncludingThis (mallocSizeOf);
112134 }
113135
114- [[nodiscard]] bool put (const Key& k, const Value& v) {
115- auto p = map.lookupForAdd (k);
116- if (p) {
117- if (!JS::GCPolicy<Key>::isTenured (k) ||
118- !JS::GCPolicy<Value>::isTenured (v)) {
119- if (!nurseryEntries.append (k)) {
120- return false ;
121- }
122- }
123- p->value () = v;
124- return true ;
125- }
126-
127- bool ok = map.add (p, k, v);
128- if (!ok) {
136+ [[nodiscard]] bool put (const Key& key, const Value& value) {
137+ if ((!key->isTenured () || !value->isTenured ()) &&
138+ !nurseryEntries.append (key)) {
129139 return false ;
130140 }
131141
132- if (!JS::GCPolicy<Key>::isTenured (k) ||
133- !JS::GCPolicy<Value>::isTenured (v)) {
134- if (!nurseryEntries.append (k)) {
135- map.remove (k);
136- return false ;
137- }
142+ auto p = map.lookupForAdd (key);
143+ if (p) {
144+ p->value () = value;
145+ return true ;
138146 }
139147
140- return true ;
148+ return map. add (p, key, value) ;
141149 }
142150
143151 void sweepAfterMinorGC (JSTracer* trc) {
@@ -148,25 +156,25 @@ class NurseryAwareHashMap {
148156 }
149157
150158 // Drop the entry if the value is not marked.
151- if (JS::GCPolicy<BarrieredValue >::needsSweep (&p->value ())) {
159+ if (JS::GCPolicy<MapValue >::needsSweep (&p->value ())) {
152160 map.remove (key);
153161 continue ;
154162 }
155163
156164 // Update and relocate the key, if the value is still needed.
157165 //
158- // Non-string Values will contain a strong reference to Key, as per
159- // its use in the CrossCompartmentWrapperMap, so the key will never
160- // be dying here. Strings do *not* have any sort of pointer from
161- // wrapper to wrappee, as they are just copies. The wrapper map
162- // entry is merely used as a cache to avoid re-copying the string,
163- // and currently that entire cache is flushed on major GC.
164- Key copy (key);
165- bool sweepKey = JS::GCPolicy<Key>::needsSweep (©);
166- if (sweepKey) {
167- map.remove (key);
166+ // Non-string Values will contain a strong reference to Key, as per its
167+ // use in the CrossCompartmentWrapperMap, so the key will never be dying
168+ // here. Strings do *not* have any sort of pointer from wrapper to
169+ // wrappee, as they are just copies. The wrapper map entry is merely used
170+ // as a cache to avoid re-copying the string, and currently that entire
171+ // cache is flushed on major GC.
172+ MapKey copy (key);
173+ if (JS::GCPolicy<MapKey>::needsSweep (©)) {
174+ map.remove (p);
168175 continue ;
169176 }
177+
170178 if (AllowDuplicates) {
171179 // Drop duplicated keys.
172180 //
@@ -178,7 +186,7 @@ class NurseryAwareHashMap {
178186 } else if (map.has (copy)) {
179187 // Key was forwarded to the same place that another key was already
180188 // forwarded to.
181- map.remove (key );
189+ map.remove (p );
182190 } else {
183191 map.rekeyAs (key, copy, copy);
184192 }
@@ -203,6 +211,24 @@ class NurseryAwareHashMap {
203211} // namespace js
204212
205213namespace JS {
214+
215+ template <typename T>
216+ struct GCPolicy <js::detail::UnsafeBarePtr<T>> {
217+ static bool needsSweep (js::detail::UnsafeBarePtr<T>* vp) {
218+ if (*vp) {
219+ return js::gc::IsAboutToBeFinalizedUnbarriered (vp->unbarrieredAddress ());
220+ }
221+ return false ;
222+ }
223+ static bool traceWeak (JSTracer* trc, js::detail::UnsafeBarePtr<T>* vp) {
224+ if (*vp) {
225+ return js::TraceManuallyBarrieredWeakEdge (trc, vp->unbarrieredAddress (),
226+ " UnsafeBarePtr" );
227+ }
228+ return true ;
229+ }
230+ };
231+
206232template <typename T>
207233struct GCPolicy <js::detail::UnsafeBareWeakHeapPtr<T>> {
208234 static void trace (JSTracer* trc, js::detail::UnsafeBareWeakHeapPtr<T>* thingp,
@@ -213,6 +239,15 @@ struct GCPolicy<js::detail::UnsafeBareWeakHeapPtr<T>> {
213239 return js::gc::IsAboutToBeFinalized (thingp);
214240 }
215241};
242+
216243} // namespace JS
217244
245+ namespace mozilla {
246+
247+ template <class T >
248+ struct DefaultHasher <js::detail::UnsafeBarePtr<T>>
249+ : js::detail::UnsafeBarePtrHasher<T> {};
250+
251+ } // namespace mozilla
252+
218253#endif // gc_NurseryAwareHashMap_h
0 commit comments