StableCellHasher-inl.h (6793B)
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/. */ 6 7 #ifndef gc_StableCellHasher_inl_h 8 #define gc_StableCellHasher_inl_h 9 10 #include "gc/StableCellHasher.h" 11 12 #include "mozilla/HashFunctions.h" 13 14 #include "gc/Cell.h" 15 #include "gc/Marking.h" 16 #include "gc/Zone.h" 17 #include "vm/JSObject.h" 18 #include "vm/NativeObject.h" 19 #include "vm/Runtime.h" 20 21 namespace js { 22 namespace gc { 23 24 extern uint64_t NextCellUniqueId(JSRuntime* rt); 25 26 inline bool MaybeGetUniqueId(Cell* cell, uint64_t* uidp) { 27 MOZ_ASSERT(uidp); 28 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread()) || 29 CurrentThreadIsPerformingGC()); 30 31 if (cell->is<JSObject>()) { 32 JSObject* obj = cell->as<JSObject>(); 33 if (obj->is<NativeObject>()) { 34 auto* nobj = &obj->as<NativeObject>(); 35 if (!nobj->hasUniqueId()) { 36 return false; 37 } 38 39 *uidp = nobj->uniqueId(); 40 return true; 41 } 42 } 43 44 // Get an existing uid, if one has been set. 45 auto p = cell->zone()->uniqueIds().readonlyThreadsafeLookup(cell); 46 if (!p) { 47 return false; 48 } 49 50 *uidp = p->value(); 51 52 return true; 53 } 54 55 extern bool CreateUniqueIdForNativeObject(NativeObject* obj, uint64_t* uidp); 56 extern bool CreateUniqueIdForNonNativeObject(Cell* cell, UniqueIdMap::AddPtr, 57 uint64_t* uidp); 58 59 inline bool GetOrCreateUniqueId(Cell* cell, uint64_t* uidp) { 60 MOZ_ASSERT(uidp); 61 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread()) || 62 CurrentThreadIsPerformingGC()); 63 64 if (cell->is<JSObject>()) { 65 JSObject* obj = cell->as<JSObject>(); 66 if (obj->is<NativeObject>()) { 67 auto* nobj = &obj->as<NativeObject>(); 68 if (nobj->hasUniqueId()) { 69 *uidp = nobj->uniqueId(); 70 return true; 71 } 72 73 return CreateUniqueIdForNativeObject(nobj, uidp); 74 } 75 } 76 77 // Get an existing uid, if one has been set. 78 auto p = cell->zone()->uniqueIds().lookupForAdd(cell); 79 if (p) { 80 *uidp = p->value(); 81 return true; 82 } 83 84 return CreateUniqueIdForNonNativeObject(cell, p, uidp); 85 } 86 87 inline bool SetOrUpdateUniqueId(JSContext* cx, Cell* cell, uint64_t uid) { 88 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread())); 89 90 if (cell->is<JSObject>()) { 91 JSObject* obj = cell->as<JSObject>(); 92 if (obj->is<NativeObject>()) { 93 auto* nobj = &obj->as<NativeObject>(); 94 return nobj->setOrUpdateUniqueId(cx, uid); 95 } 96 } 97 98 // If the cell was in the nursery, hopefully unlikely, then we need to 99 // tell the nursery about it so that it can sweep the uid if the thing 100 // does not get tenured. 101 JSRuntime* runtime = cell->runtimeFromMainThread(); 102 if (IsInsideNursery(cell) && 103 !runtime->gc.nursery().addedUniqueIdToCell(cell)) { 104 return false; 105 } 106 107 return cell->zone()->uniqueIds().put(cell, uid); 108 } 109 110 inline uint64_t GetUniqueIdInfallible(Cell* cell) { 111 uint64_t uid; 112 AutoEnterOOMUnsafeRegion oomUnsafe; 113 if (!GetOrCreateUniqueId(cell, &uid)) { 114 oomUnsafe.crash("failed to allocate uid"); 115 } 116 return uid; 117 } 118 119 inline bool HasUniqueId(Cell* cell) { 120 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread()) || 121 CurrentThreadIsPerformingGC()); 122 123 if (cell->is<JSObject>()) { 124 JSObject* obj = cell->as<JSObject>(); 125 if (obj->is<NativeObject>()) { 126 return obj->as<NativeObject>().hasUniqueId(); 127 } 128 } 129 130 return cell->zone()->uniqueIds().has(cell); 131 } 132 133 inline void TransferUniqueId(Cell* tgt, Cell* src) { 134 MOZ_ASSERT(src != tgt); 135 MOZ_ASSERT(CurrentThreadCanAccessRuntime(tgt->runtimeFromAnyThread())); 136 MOZ_ASSERT(src->zone() == tgt->zone()); 137 138 Zone* zone = tgt->zone(); 139 MOZ_ASSERT_IF(zone->uniqueIds().has(src), !zone->uniqueIds().has(tgt)); 140 zone->uniqueIds().rekeyIfMoved(src, tgt); 141 } 142 143 inline void RemoveUniqueId(Cell* cell) { 144 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread())); 145 // The cell may no longer be in the hash table if it was swapped with a 146 // NativeObject. 147 cell->zone()->uniqueIds().remove(cell); 148 } 149 150 } // namespace gc 151 152 static inline js::HashNumber UniqueIdToHash(uint64_t uid) { 153 // This uses the default hasher which returns the lower 32 bits of 64 bit 154 // integers as the hash code. This is OK because he result will be scrambled 155 // later by ScrambleHashCode(). 156 return DefaultHasher<uint64_t>::hash(uid); 157 } 158 159 template <typename T> 160 /* static */ bool StableCellHasher<T>::maybeGetHash(const Lookup& l, 161 HashNumber* hashOut) { 162 if (!l) { 163 *hashOut = 0; 164 return true; 165 } 166 167 uint64_t uid; 168 if (!gc::MaybeGetUniqueId(l, &uid)) { 169 return false; 170 } 171 172 *hashOut = UniqueIdToHash(uid); 173 return true; 174 } 175 176 template <typename T> 177 /* static */ bool StableCellHasher<T>::ensureHash(const Lookup& l, 178 HashNumber* hashOut) { 179 if (!l) { 180 *hashOut = 0; 181 return true; 182 } 183 184 uint64_t uid; 185 if (!gc::GetOrCreateUniqueId(l, &uid)) { 186 return false; 187 } 188 189 *hashOut = UniqueIdToHash(uid); 190 return true; 191 } 192 193 template <typename T> 194 /* static */ HashNumber StableCellHasher<T>::hash(const Lookup& l) { 195 if (!l) { 196 return 0; 197 } 198 199 // We have to access the zone from-any-thread here: a worker thread may be 200 // cloning a self-hosted object from the main runtime's self- hosting zone 201 // into another runtime. The zone's uid lock will protect against multiple 202 // workers doing this simultaneously. 203 MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || 204 CurrentThreadIsPerformingGC()); 205 206 return UniqueIdToHash(gc::GetUniqueIdInfallible(l)); 207 } 208 209 template <typename T> 210 /* static */ bool StableCellHasher<T>::match(const Key& k, const Lookup& l) { 211 if (k == l) { 212 return true; 213 } 214 215 if (!k || !l) { 216 return false; 217 } 218 219 MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || 220 CurrentThreadIsPerformingGC()); 221 222 #ifdef DEBUG 223 // Incremental table sweeping means that existing table entries may no 224 // longer have unique IDs. We fail the match in that case and the entry is 225 // removed from the table later on. 226 if (!gc::HasUniqueId(k)) { 227 Key key = k; 228 MOZ_ASSERT(key->zoneFromAnyThread()->needsIncrementalBarrier() && 229 !key->isMarkedAny()); 230 } 231 MOZ_ASSERT(gc::HasUniqueId(l)); 232 #endif 233 234 uint64_t keyId; 235 if (!gc::MaybeGetUniqueId(k, &keyId)) { 236 // Key is dead and cannot match lookup which must be live. 237 return false; 238 } 239 240 return keyId == gc::GetUniqueIdInfallible(l); 241 } 242 243 } // namespace js 244 245 #endif // gc_StableCellHasher_inl_h