diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/gc/StableCellHasher-inl.h | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/js/src/gc/StableCellHasher-inl.h b/js/src/gc/StableCellHasher-inl.h new file mode 100644 index 0000000000..f76748afef --- /dev/null +++ b/js/src/gc/StableCellHasher-inl.h @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef gc_StableCellHasher_inl_h +#define gc_StableCellHasher_inl_h + +#include "gc/StableCellHasher.h" + +#include "mozilla/HashFunctions.h" + +#include "gc/Cell.h" +#include "gc/Marking.h" +#include "gc/Zone.h" +#include "vm/JSObject.h" +#include "vm/NativeObject.h" +#include "vm/Runtime.h" + +namespace js { +namespace gc { + +extern uint64_t NextCellUniqueId(JSRuntime* rt); + +inline bool MaybeGetUniqueId(Cell* cell, uint64_t* uidp) { + MOZ_ASSERT(uidp); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread()) || + CurrentThreadIsPerformingGC()); + + if (cell->is<JSObject>()) { + JSObject* obj = cell->as<JSObject>(); + if (obj->is<NativeObject>()) { + auto* nobj = &obj->as<NativeObject>(); + if (!nobj->hasUniqueId()) { + return false; + } + + *uidp = nobj->uniqueId(); + return true; + } + } + + // Get an existing uid, if one has been set. + auto p = cell->zone()->uniqueIds().readonlyThreadsafeLookup(cell); + if (!p) { + return false; + } + + *uidp = p->value(); + + return true; +} + +extern bool CreateUniqueIdForNativeObject(NativeObject* obj, uint64_t* uidp); +extern bool CreateUniqueIdForNonNativeObject(Cell* cell, UniqueIdMap::AddPtr, + uint64_t* uidp); + +inline bool GetOrCreateUniqueId(Cell* cell, uint64_t* uidp) { + MOZ_ASSERT(uidp); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread()) || + CurrentThreadIsPerformingGC()); + + if (cell->is<JSObject>()) { + JSObject* obj = cell->as<JSObject>(); + if (obj->is<NativeObject>()) { + auto* nobj = &obj->as<NativeObject>(); + if (nobj->hasUniqueId()) { + *uidp = nobj->uniqueId(); + return true; + } + + return CreateUniqueIdForNativeObject(nobj, uidp); + } + } + + // Get an existing uid, if one has been set. + auto p = cell->zone()->uniqueIds().lookupForAdd(cell); + if (p) { + *uidp = p->value(); + return true; + } + + return CreateUniqueIdForNonNativeObject(cell, p, uidp); +} + +inline bool SetOrUpdateUniqueId(JSContext* cx, Cell* cell, uint64_t uid) { + MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread())); + + if (cell->is<JSObject>()) { + JSObject* obj = cell->as<JSObject>(); + if (obj->is<NativeObject>()) { + auto* nobj = &obj->as<NativeObject>(); + return nobj->setOrUpdateUniqueId(cx, uid); + } + } + + // If the cell was in the nursery, hopefully unlikely, then we need to + // tell the nursery about it so that it can sweep the uid if the thing + // does not get tenured. + JSRuntime* runtime = cell->runtimeFromMainThread(); + if (IsInsideNursery(cell) && + !runtime->gc.nursery().addedUniqueIdToCell(cell)) { + return false; + } + + return cell->zone()->uniqueIds().put(cell, uid); +} + +inline uint64_t GetUniqueIdInfallible(Cell* cell) { + uint64_t uid; + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!GetOrCreateUniqueId(cell, &uid)) { + oomUnsafe.crash("failed to allocate uid"); + } + return uid; +} + +inline bool HasUniqueId(Cell* cell) { + MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread()) || + CurrentThreadIsPerformingGC()); + + if (cell->is<JSObject>()) { + JSObject* obj = cell->as<JSObject>(); + if (obj->is<NativeObject>()) { + return obj->as<NativeObject>().hasUniqueId(); + } + } + + return cell->zone()->uniqueIds().has(cell); +} + +inline void TransferUniqueId(Cell* tgt, Cell* src) { + MOZ_ASSERT(src != tgt); + MOZ_ASSERT(!IsInsideNursery(tgt)); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(tgt->runtimeFromAnyThread())); + MOZ_ASSERT(src->zone() == tgt->zone()); + + Zone* zone = tgt->zone(); + MOZ_ASSERT(!zone->uniqueIds().has(tgt)); + zone->uniqueIds().rekeyIfMoved(src, tgt); +} + +inline void RemoveUniqueId(Cell* cell) { + MOZ_ASSERT(CurrentThreadCanAccessRuntime(cell->runtimeFromAnyThread())); + // The cell may no longer be in the hash table if it was swapped with a + // NativeObject. + cell->zone()->uniqueIds().remove(cell); +} + +} // namespace gc + +static inline js::HashNumber UniqueIdToHash(uint64_t uid) { + // This uses the default hasher which returns the lower 32 bits of 64 bit + // integers as the hash code. This is OK because he result will be scrambled + // later by ScrambleHashCode(). + return DefaultHasher<uint64_t>::hash(uid); +} + +template <typename T> +/* static */ bool StableCellHasher<T>::maybeGetHash(const Lookup& l, + HashNumber* hashOut) { + if (!l) { + *hashOut = 0; + return true; + } + + uint64_t uid; + if (!gc::MaybeGetUniqueId(l, &uid)) { + return false; + } + + *hashOut = UniqueIdToHash(uid); + return true; +} + +template <typename T> +/* static */ bool StableCellHasher<T>::ensureHash(const Lookup& l, + HashNumber* hashOut) { + if (!l) { + *hashOut = 0; + return true; + } + + uint64_t uid; + if (!gc::GetOrCreateUniqueId(l, &uid)) { + return false; + } + + *hashOut = UniqueIdToHash(uid); + return true; +} + +template <typename T> +/* static */ HashNumber StableCellHasher<T>::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 runtime's self- hosting zone + // into another runtime. The zone's uid lock will protect against multiple + // workers doing this simultaneously. + MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || + CurrentThreadIsPerformingGC()); + + return UniqueIdToHash(gc::GetUniqueIdInfallible(l)); +} + +template <typename T> +/* static */ bool StableCellHasher<T>::match(const Key& k, const Lookup& l) { + if (k == l) { + return true; + } + + if (!k || !l) { + return false; + } + + MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || + CurrentThreadIsPerformingGC()); + +#ifdef DEBUG + // Incremental table sweeping means that existing table entries may no + // longer have unique IDs. We fail the match in that case and the entry is + // removed from the table later on. + if (!gc::HasUniqueId(k)) { + Key key = k; + MOZ_ASSERT(IsAboutToBeFinalizedUnbarriered(key)); + } + MOZ_ASSERT(gc::HasUniqueId(l)); +#endif + + uint64_t keyId; + if (!gc::MaybeGetUniqueId(k, &keyId)) { + // Key is dead and cannot match lookup which must be live. + return false; + } + + return keyId == gc::GetUniqueIdInfallible(l); +} + +} // namespace js + +#endif // gc_StableCellHasher_inl_h |