diff --git a/js/public/HashTable.h b/js/public/HashTable.h index a251886ecbd0..16693bbed38d 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -740,6 +740,7 @@ class HashTableEntry } T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); } + NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); } bool isFree() const { return keyHash == sFreeKey; } void clearLive() { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } @@ -979,6 +980,16 @@ class HashTable : private AllocPolicy #endif } + NonConstT& mutableFront() { + MOZ_ASSERT(!this->empty()); +#ifdef JS_DEBUG + MOZ_ASSERT(this->validEntry); + MOZ_ASSERT(this->generation == this->Range::table_->generation()); + MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount); +#endif + return this->cur->getMutable(); + } + // Removes the |front()| element and re-inserts it into the table with // a new key at the new Lookup position. |front()| is invalid after // this operation until the next call to |popFront()|. diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index c361d97f7dae..4014f4a8e6fc 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -10,7 +10,9 @@ #include "jsobj.h" #include "gc/Zone.h" +#include "js/HashTable.h" #include "js/Value.h" +#include "vm/ScopeObject.h" #include "vm/Symbol.h" #ifdef DEBUG @@ -102,3 +104,56 @@ JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next MOZ_ASSERT(valuep); js::InternalGCMethods::postBarrier(valuep, prev, next); } + +template +/* static */ js::HashNumber +js::MovableCellHasher::hash(const Lookup& l) +{ + if (!l) + return 0; + + // We have to access the zone from-any-thread here: a worker thread may be + // cloning a self-hosted object from the main-thread-runtime-owned self- + // hosting zone into the off-main-thread runtime. The zone's uid lock will + // protect against multiple workers doing this simultaneously. + MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || + l->zoneFromAnyThread()->isSelfHostingZone()); + + js::HashNumber hn; + if (!l->zoneFromAnyThread()->getHashCode(l, &hn)) + js::CrashAtUnhandlableOOM("failed to get a stable hash code"); + return hn; +} + +template +/* static */ bool +js::MovableCellHasher::match(const Key& k, const Lookup& l) +{ + // Return true if both are null or false if only one is null. + if (!k) + return !l; + if (!l) + return false; + + MOZ_ASSERT(k); + MOZ_ASSERT(l); + MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || + l->zoneFromAnyThread()->isSelfHostingZone()); + + Zone* zone = k->zoneFromAnyThread(); + if (zone != l->zoneFromAnyThread()) + return false; + MOZ_ASSERT(zone->hasUniqueId(k)); + MOZ_ASSERT(zone->hasUniqueId(l)); + + // Since both already have a uid (from hash), the get is infallible. + uint64_t uidK, uidL; + MOZ_ALWAYS_TRUE(zone->getUniqueId(k, &uidK)); + MOZ_ALWAYS_TRUE(zone->getUniqueId(l, &uidL)); + return uidK == uidL; +} + +template struct js::MovableCellHasher; +template struct js::MovableCellHasher; +template struct js::MovableCellHasher; +template struct js::MovableCellHasher; diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index ec8c00debaff..f80a918e4465 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -693,6 +693,31 @@ class ImmutableTenuredPtr const T* address() { return &value; } }; +// Provide hash codes for Cell kinds that may be relocated and, thus, not have +// a stable address to use as the base for a hash code. Instead of the address, +// this hasher uses Cell::getUniqueId to provide exact matches and as a base +// for generating hash codes. +// +// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr +// would not likely be a useful key, there are some cases where being able to +// hash a nullptr is useful, either on purpose or because of bugs: +// (1) existence checks where the key may happen to be null and (2) some +// aggregate Lookup kinds embed a JSObject* that is frequently null and do not +// null test before dispatching to the hasher. +template +struct MovableCellHasher +{ + static_assert(mozilla::IsBaseOf::Type>::value, + "MovableCellHasher's T must be a Cell type that may move"); + + using Key = T; + using Lookup = T; + + static HashNumber hash(const Lookup& l); + static bool match(const Key& k, const Lookup& l); + static void rekey(Key& k, const Key& newKey) { k = newKey; } +}; + /* Useful for hashtables with a HeapPtr as key. */ template struct HeapPtrHasher