summaryrefslogtreecommitdiffstats
path: root/js/src/jsweakmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsweakmap.cpp')
-rw-r--r--js/src/jsweakmap.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp
new file mode 100644
index 000000000..1d43bd285
--- /dev/null
+++ b/js/src/jsweakmap.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jsweakmap.h"
+
+#include <string.h>
+
+#include "jsapi.h"
+#include "jscntxt.h"
+#include "jsfriendapi.h"
+#include "jsobj.h"
+#include "jswrapper.h"
+
+#include "js/GCAPI.h"
+#include "vm/GlobalObject.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+using namespace js::gc;
+
+WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
+ : memberOf(memOf),
+ zone(zone),
+ marked(false)
+{
+ MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
+}
+
+WeakMapBase::~WeakMapBase()
+{
+ MOZ_ASSERT(CurrentThreadIsGCSweeping());
+}
+
+void
+WeakMapBase::unmarkZone(JS::Zone* zone)
+{
+ for (WeakMapBase* m : zone->gcWeakMapList)
+ m->marked = false;
+}
+
+void
+WeakMapBase::markAll(JS::Zone* zone, JSTracer* tracer)
+{
+ MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
+ for (WeakMapBase* m : zone->gcWeakMapList) {
+ m->trace(tracer);
+ TraceNullableEdge(tracer, &m->memberOf, "memberOf");
+ }
+}
+
+bool
+WeakMapBase::markZoneIteratively(JS::Zone* zone, JSTracer* tracer)
+{
+ bool markedAny = false;
+ for (WeakMapBase* m : zone->gcWeakMapList) {
+ if (m->marked && m->traceEntries(tracer))
+ markedAny = true;
+ }
+ return markedAny;
+}
+
+bool
+WeakMapBase::findInterZoneEdges(JS::Zone* zone)
+{
+ for (WeakMapBase* m : zone->gcWeakMapList) {
+ if (!m->findZoneEdges())
+ return false;
+ }
+ return true;
+}
+
+void
+WeakMapBase::sweepZone(JS::Zone* zone)
+{
+ for (WeakMapBase* m = zone->gcWeakMapList.getFirst(); m; ) {
+ WeakMapBase* next = m->getNext();
+ if (m->marked) {
+ m->sweep();
+ } else {
+ /* Destroy the hash map now to catch any use after this point. */
+ m->finish();
+ m->removeFrom(zone->gcWeakMapList);
+ }
+ m = next;
+ }
+
+#ifdef DEBUG
+ for (WeakMapBase* m : zone->gcWeakMapList)
+ MOZ_ASSERT(m->isInList() && m->marked);
+#endif
+}
+
+void
+WeakMapBase::traceAllMappings(WeakMapTracer* tracer)
+{
+ JSRuntime* rt = tracer->context;
+ 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, WeakMapSet& markedWeakMaps)
+{
+ for (WeakMapBase* m : zone->gcWeakMapList) {
+ if (m->marked && !markedWeakMaps.put(m))
+ return false;
+ }
+ return true;
+}
+
+void
+WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
+{
+ for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
+ WeakMapBase* map = r.front();
+ MOZ_ASSERT(map->zone->isGCMarking());
+ MOZ_ASSERT(!map->marked);
+ map->marked = true;
+ }
+}
+
+bool
+ObjectValueMap::findZoneEdges()
+{
+ /*
+ * For unmarked weakmap keys with delegates in a different zone, add a zone
+ * edge to ensure that the delegate zone finishes marking before the key
+ * zone.
+ */
+ JS::AutoSuppressGCAnalysis nogc;
+ for (Range r = all(); !r.empty(); r.popFront()) {
+ JSObject* key = r.front().key();
+ if (key->asTenured().isMarked(BLACK) && !key->asTenured().isMarked(GRAY))
+ continue;
+ JSObject* delegate = getDelegate(key);
+ if (!delegate)
+ continue;
+ Zone* delegateZone = delegate->zone();
+ if (delegateZone == zone || !delegateZone->isGCMarking())
+ continue;
+ if (!delegateZone->gcZoneGroupEdges.put(key->zone()))
+ return false;
+ }
+ return true;
+}
+
+ObjectWeakMap::ObjectWeakMap(JSContext* cx)
+ : map(cx, nullptr)
+{}
+
+bool
+ObjectWeakMap::init()
+{
+ return map.init();
+}
+
+JSObject*
+ObjectWeakMap::lookup(const JSObject* obj)
+{
+ MOZ_ASSERT(map.initialized());
+ if (ObjectValueMap::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);
+ MOZ_ASSERT(map.initialized());
+
+ MOZ_ASSERT(!map.has(obj));
+ if (!map.put(obj, ObjectValue(*target))) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+void
+ObjectWeakMap::clear()
+{
+ MOZ_ASSERT(map.initialized());
+ map.clear();
+}
+
+void
+ObjectWeakMap::trace(JSTracer* trc)
+{
+ MOZ_ASSERT(map.initialized());
+ map.trace(trc);
+}
+
+size_t
+ObjectWeakMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+ MOZ_ASSERT(map.initialized());
+ return map.sizeOfExcludingThis(mallocSizeOf);
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+void
+ObjectWeakMap::checkAfterMovingGC()
+{
+ MOZ_ASSERT(map.initialized());
+ for (ObjectValueMap::Range r = map.all(); !r.empty(); r.popFront()) {
+ CheckGCThingAfterMovingGC(r.front().key().get());
+ CheckGCThingAfterMovingGC(&r.front().value().toObject());
+ }
+}
+#endif // JSGC_HASH_TABLE_CHECKS