summaryrefslogtreecommitdiffstats
path: root/js/src/gc/PublicIterators.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/gc/PublicIterators.h197
1 files changed, 197 insertions, 0 deletions
diff --git a/js/src/gc/PublicIterators.h b/js/src/gc/PublicIterators.h
new file mode 100644
index 0000000000..3df7926438
--- /dev/null
+++ b/js/src/gc/PublicIterators.h
@@ -0,0 +1,197 @@
+/* -*- 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/. */
+
+/*
+ * Iterators for various data structures.
+ */
+
+#ifndef gc_PublicIterators_h
+#define gc_PublicIterators_h
+
+#include "mozilla/Maybe.h"
+
+#include "jstypes.h"
+#include "gc/GCRuntime.h"
+#include "gc/IteratorUtils.h"
+#include "gc/Zone.h"
+#include "vm/Compartment.h"
+#include "vm/Runtime.h"
+
+struct JSRuntime;
+
+namespace JS {
+class JS_PUBLIC_API Realm;
+}
+
+namespace js {
+
+// Accessing the atoms zone can be dangerous because helper threads may be
+// accessing it concurrently to the main thread, so it's better to skip the
+// atoms zone when iterating over zones. If you need to iterate over the atoms
+// zone, consider using AutoLockAllAtoms.
+enum ZoneSelector { WithAtoms, SkipAtoms };
+
+// Iterate over all zones in the runtime apart from the atoms zone and those
+// which may be in use by parse threads.
+class NonAtomZonesIter {
+ gc::AutoEnterIteration iterMarker;
+ JS::Zone** it;
+ JS::Zone** end;
+
+ public:
+ explicit NonAtomZonesIter(gc::GCRuntime* gc)
+ : iterMarker(gc), it(gc->zones().begin()), end(gc->zones().end()) {
+ skipHelperThreadZones();
+ }
+ explicit NonAtomZonesIter(JSRuntime* rt) : NonAtomZonesIter(&rt->gc) {}
+
+ bool done() const { return it == end; }
+
+ void next() {
+ MOZ_ASSERT(!done());
+ it++;
+ skipHelperThreadZones();
+ }
+
+ void skipHelperThreadZones() {
+ while (!done() && get()->usedByHelperThread()) {
+ it++;
+ }
+ }
+
+ JS::Zone* get() const {
+ MOZ_ASSERT(!done());
+ return *it;
+ }
+
+ operator JS::Zone*() const { return get(); }
+ JS::Zone* operator->() const { return get(); }
+};
+
+// Iterate over all zones in the runtime, except those which may be in use by
+// parse threads. May or may not include the atoms zone.
+class ZonesIter {
+ JS::Zone* atomsZone;
+ NonAtomZonesIter otherZones;
+
+ public:
+ ZonesIter(gc::GCRuntime* gc, ZoneSelector selector)
+ : atomsZone(selector == WithAtoms ? gc->atomsZone.ref() : nullptr),
+ otherZones(gc) {}
+ ZonesIter(JSRuntime* rt, ZoneSelector selector)
+ : ZonesIter(&rt->gc, selector) {}
+
+ bool done() const { return !atomsZone && otherZones.done(); }
+
+ JS::Zone* get() const {
+ MOZ_ASSERT(!done());
+ return atomsZone ? atomsZone : otherZones.get();
+ }
+
+ void next() {
+ MOZ_ASSERT(!done());
+ if (atomsZone) {
+ atomsZone = nullptr;
+ return;
+ }
+
+ otherZones.next();
+ }
+
+ operator JS::Zone*() const { return get(); }
+ JS::Zone* operator->() const { return get(); }
+};
+
+// Iterate over all zones in the runtime, except those which may be in use by
+// parse threads.
+class AllZonesIter : public ZonesIter {
+ public:
+ explicit AllZonesIter(gc::GCRuntime* gc) : ZonesIter(gc, WithAtoms) {}
+ explicit AllZonesIter(JSRuntime* rt) : AllZonesIter(&rt->gc) {}
+};
+
+struct CompartmentsInZoneIter {
+ explicit CompartmentsInZoneIter(JS::Zone* zone) : zone(zone) {
+ it = zone->compartments().begin();
+ }
+
+ bool done() const {
+ MOZ_ASSERT(it);
+ return it < zone->compartments().begin() ||
+ it >= zone->compartments().end();
+ }
+ void next() {
+ MOZ_ASSERT(!done());
+ it++;
+ }
+
+ JS::Compartment* get() const {
+ MOZ_ASSERT(it);
+ return *it;
+ }
+
+ operator JS::Compartment*() const { return get(); }
+ JS::Compartment* operator->() const { return get(); }
+
+ private:
+ JS::Zone* zone;
+ JS::Compartment** it;
+};
+
+class RealmsInCompartmentIter {
+ JS::Compartment* comp;
+ JS::Realm** it;
+
+ public:
+ explicit RealmsInCompartmentIter(JS::Compartment* comp) : comp(comp) {
+ it = comp->realms().begin();
+ MOZ_ASSERT(!done(), "Compartments must have at least one realm");
+ }
+
+ bool done() const {
+ MOZ_ASSERT(it);
+ return it < comp->realms().begin() || it >= comp->realms().end();
+ }
+ void next() {
+ MOZ_ASSERT(!done());
+ it++;
+ }
+
+ JS::Realm* get() const {
+ MOZ_ASSERT(!done());
+ return *it;
+ }
+
+ operator JS::Realm*() const { return get(); }
+ JS::Realm* operator->() const { return get(); }
+};
+
+using RealmsInZoneIter =
+ NestedIterator<CompartmentsInZoneIter, RealmsInCompartmentIter>;
+
+// This iterator iterates over all the compartments or realms in a given set of
+// zones. The set of zones is determined by iterating ZoneIterT. The set of
+// compartments or realms is determined by InnerIterT.
+template <class ZonesIterT, class InnerIterT>
+class CompartmentsOrRealmsIterT
+ : public NestedIterator<ZonesIterT, InnerIterT> {
+ gc::AutoEnterIteration iterMarker;
+
+ public:
+ explicit CompartmentsOrRealmsIterT(gc::GCRuntime* gc)
+ : NestedIterator<ZonesIterT, InnerIterT>(gc), iterMarker(gc) {}
+ explicit CompartmentsOrRealmsIterT(JSRuntime* rt)
+ : CompartmentsOrRealmsIterT(&rt->gc) {}
+};
+
+using CompartmentsIter =
+ CompartmentsOrRealmsIterT<NonAtomZonesIter, CompartmentsInZoneIter>;
+using RealmsIter =
+ CompartmentsOrRealmsIterT<NonAtomZonesIter, RealmsInZoneIter>;
+
+} // namespace js
+
+#endif // gc_PublicIterators_h