16
16
namespace js {
17
17
18
18
namespace 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
+
19
42
// This class only handles the incremental case and does not deal with nursery
20
43
// pointers. The only users should be for NurseryAwareHashMap; it is defined
21
44
// externally because we need a GCPolicy for its use in the contained map.
@@ -63,24 +86,23 @@ enum : bool { DuplicatesNotPossible, DuplicatesPossible };
63
86
// hash table treats such edges strongly.
64
87
//
65
88
// 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.
71
93
template <typename Key, typename Value,
72
- typename HashPolicy = DefaultHasher<Key>,
73
94
typename AllocPolicy = TempAllocPolicy,
74
95
bool AllowDuplicates = DuplicatesNotPossible>
75
96
class 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>;
79
101
MapType map;
80
102
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.
84
106
Vector<Key, 0 , AllocPolicy> nurseryEntries;
85
107
86
108
public:
@@ -111,33 +133,19 @@ class NurseryAwareHashMap {
111
133
nurseryEntries.sizeOfIncludingThis (mallocSizeOf);
112
134
}
113
135
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)) {
129
139
return false ;
130
140
}
131
141
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 ;
138
146
}
139
147
140
- return true ;
148
+ return map. add (p, key, value) ;
141
149
}
142
150
143
151
void sweepAfterMinorGC (JSTracer* trc) {
@@ -148,25 +156,25 @@ class NurseryAwareHashMap {
148
156
}
149
157
150
158
// 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 ())) {
152
160
map.remove (key);
153
161
continue ;
154
162
}
155
163
156
164
// Update and relocate the key, if the value is still needed.
157
165
//
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);
168
175
continue ;
169
176
}
177
+
170
178
if (AllowDuplicates) {
171
179
// Drop duplicated keys.
172
180
//
@@ -178,7 +186,7 @@ class NurseryAwareHashMap {
178
186
} else if (map.has (copy)) {
179
187
// Key was forwarded to the same place that another key was already
180
188
// forwarded to.
181
- map.remove (key );
189
+ map.remove (p );
182
190
} else {
183
191
map.rekeyAs (key, copy, copy);
184
192
}
@@ -203,6 +211,24 @@ class NurseryAwareHashMap {
203
211
} // namespace js
204
212
205
213
namespace 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
+
206
232
template <typename T>
207
233
struct GCPolicy <js::detail::UnsafeBareWeakHeapPtr<T>> {
208
234
static void trace (JSTracer* trc, js::detail::UnsafeBareWeakHeapPtr<T>* thingp,
@@ -213,6 +239,15 @@ struct GCPolicy<js::detail::UnsafeBareWeakHeapPtr<T>> {
213
239
return js::gc::IsAboutToBeFinalized (thingp);
214
240
}
215
241
};
242
+
216
243
} // namespace JS
217
244
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
+
218
253
#endif // gc_NurseryAwareHashMap_h
0 commit comments