summaryrefslogtreecommitdiffstats
path: root/js/src/gc/WeakMap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/gc/WeakMap.cpp')
-rw-r--r--js/src/gc/WeakMap.cpp239
1 files changed, 239 insertions, 0 deletions
diff --git a/js/src/gc/WeakMap.cpp b/js/src/gc/WeakMap.cpp
new file mode 100644
index 0000000000..749ea52937
--- /dev/null
+++ b/js/src/gc/WeakMap.cpp
@@ -0,0 +1,239 @@
+/* -*- 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/. */
+
+#include "gc/WeakMap-inl.h"
+
+#include <string.h>
+
+#include "gc/PublicIterators.h"
+#include "vm/JSObject.h"
+
+#include "gc/Marking-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
+ : memberOf(memOf), zone_(zone) {
+ MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
+ MOZ_ASSERT(!IsMarked(mapColor()));
+}
+
+WeakMapBase::~WeakMapBase() {
+ MOZ_ASSERT(CurrentThreadIsGCFinalizing() ||
+ CurrentThreadCanAccessZone(zone_));
+}
+
+void WeakMapBase::unmarkZone(JS::Zone* zone) {
+ AutoEnterOOMUnsafeRegion oomUnsafe;
+ if (!zone->gcEphemeronEdges().clear()) {
+ oomUnsafe.crash("clearing ephemeron edges table");
+ }
+ MOZ_ASSERT(zone->gcNurseryEphemeronEdges().count() == 0);
+
+ for (WeakMapBase* m : zone->gcWeakMapList()) {
+ m->setMapColor(CellColor::White);
+ }
+}
+
+void Zone::traceWeakMaps(JSTracer* trc) {
+ MOZ_ASSERT(trc->weakMapAction() != JS::WeakMapTraceAction::Skip);
+ for (WeakMapBase* m : gcWeakMapList()) {
+ m->trace(trc);
+ TraceNullableEdge(trc, &m->memberOf, "memberOf");
+ }
+}
+
+bool WeakMapBase::markMap(MarkColor markColor) {
+ // We may be marking in parallel here so use a compare exchange loop to handle
+ // concurrent updates to the map color.
+ //
+ // The color increases monotonically; we don't downgrade from black to gray.
+ //
+ // We can attempt to mark gray after marking black when a barrier pushes the
+ // map object onto the black mark stack when it's already present on the
+ // gray mark stack, since this is marked later.
+
+ uint32_t targetColor = uint32_t(markColor);
+
+ for (;;) {
+ uint32_t currentColor = mapColor_;
+
+ if (currentColor >= targetColor) {
+ return false;
+ }
+
+ if (mapColor_.compareExchange(currentColor, targetColor)) {
+ return true;
+ }
+ }
+}
+
+bool WeakMapBase::addImplicitEdges(MarkColor mapColor, Cell* key,
+ Cell* delegate, TenuredCell* value) {
+ if (delegate) {
+ return addEphemeronTableEntries(mapColor, delegate, key, value);
+ }
+
+ if (value) {
+ return addEphemeronTableEntries(mapColor, key, value, nullptr);
+ }
+
+ return true;
+}
+
+bool WeakMapBase::addEphemeronTableEntries(MarkColor mapColor, gc::Cell* key,
+ gc::Cell* value1,
+ gc::Cell* maybeValue2) {
+ // Add implicit edges from |key| to |value1| and |maybeValue2| if supplied.
+ //
+ // Note the key and values do not necessarily correspond to the weak map
+ // entry's key and value at this point.
+
+ auto& edgeTable = key->zone()->gcEphemeronEdges(key);
+ auto* ptr = edgeTable.getOrAdd(key);
+ if (!ptr) {
+ return false;
+ }
+
+ if (!ptr->value.emplaceBack(mapColor, value1)) {
+ return false;
+ }
+
+ if (maybeValue2 && !ptr->value.emplaceBack(mapColor, maybeValue2)) {
+ return false;
+ }
+
+ return true;
+}
+
+#if defined(JS_GC_ZEAL) || defined(DEBUG)
+bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) {
+ // This is called at the end of marking.
+ MOZ_ASSERT(zone->isGCMarking());
+
+ bool ok = true;
+ for (WeakMapBase* m : zone->gcWeakMapList()) {
+ if (IsMarked(m->mapColor()) && !m->checkMarking()) {
+ ok = false;
+ }
+ }
+
+ return ok;
+}
+#endif
+
+bool WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker) {
+ bool markedAny = false;
+ for (WeakMapBase* m : zone->gcWeakMapList()) {
+ if (IsMarked(m->mapColor()) && m->markEntries(marker)) {
+ markedAny = true;
+ }
+ }
+ return markedAny;
+}
+
+bool WeakMapBase::findSweepGroupEdgesForZone(JS::Zone* zone) {
+ for (WeakMapBase* m : zone->gcWeakMapList()) {
+ if (!m->findSweepGroupEdges()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void Zone::sweepWeakMaps(JSTracer* trc) {
+ for (WeakMapBase* m = gcWeakMapList().getFirst(); m;) {
+ WeakMapBase* next = m->getNext();
+ if (IsMarked(m->mapColor())) {
+ m->traceWeakEdges(trc);
+ } else {
+ m->clearAndCompact();
+ m->removeFrom(gcWeakMapList());
+ }
+ m = next;
+ }
+
+#ifdef DEBUG
+ for (WeakMapBase* m : gcWeakMapList()) {
+ MOZ_ASSERT(m->isInList() && IsMarked(m->mapColor()));
+ }
+#endif
+}
+
+void WeakMapBase::traceAllMappings(WeakMapTracer* tracer) {
+ JSRuntime* rt = tracer->runtime;
+ for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
+ for (WeakMapBase* m : zone->gcWeakMapList()) {
+ // The WeakMapTracer callback is not allowed to GC.
+ JS::AutoSuppressGCAnalysis nogc;
+ m->traceMappings(tracer);
+ }
+ }
+}
+
+bool WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone,
+ WeakMapColors& markedWeakMaps) {
+ for (WeakMapBase* m : zone->gcWeakMapList()) {
+ if (IsMarked(m->mapColor()) && !markedWeakMaps.put(m, m->mapColor())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void WeakMapBase::restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps) {
+ for (WeakMapColors::Range r = markedWeakMaps.all(); !r.empty();
+ r.popFront()) {
+ WeakMapBase* map = r.front().key();
+ MOZ_ASSERT(map->zone()->isGCMarking());
+ MOZ_ASSERT(!IsMarked(map->mapColor()));
+ map->setMapColor(r.front().value());
+ }
+}
+
+ObjectWeakMap::ObjectWeakMap(JSContext* cx) : map(cx, nullptr) {}
+
+JSObject* ObjectWeakMap::lookup(const JSObject* obj) {
+ if (ObjectValueWeakMap::Ptr p = map.lookup(const_cast<JSObject*>(obj))) {
+ return &p->value().toObject();
+ }
+ return nullptr;
+}
+
+bool ObjectWeakMap::add(JSContext* cx, JSObject* obj, JSObject* target) {
+ MOZ_ASSERT(obj && target);
+
+ Value targetVal(ObjectValue(*target));
+ if (!map.putNew(obj, targetVal)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+void ObjectWeakMap::remove(JSObject* key) {
+ MOZ_ASSERT(key);
+ map.remove(key);
+}
+
+void ObjectWeakMap::clear() { map.clear(); }
+
+void ObjectWeakMap::trace(JSTracer* trc) { map.trace(trc); }
+
+size_t ObjectWeakMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+ return map.shallowSizeOfExcludingThis(mallocSizeOf);
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+void ObjectWeakMap::checkAfterMovingGC() {
+ for (ObjectValueWeakMap::Range r = map.all(); !r.empty(); r.popFront()) {
+ CheckGCThingAfterMovingGC(r.front().key().get());
+ CheckGCThingAfterMovingGC(&r.front().value().toObject());
+ }
+}
+#endif // JSGC_HASH_TABLE_CHECKS