#ifndef gc_PublicIterators_h
#define gc_PublicIterators_h
#include "mozilla/Maybe.h"
#include "gc/Zone.h"
#include "vm/Realm.h"
namespace js {
enum ZoneSelector { WithAtoms, SkipAtoms };
class ZonesIter {
gc::AutoEnterIteration iterMarker;
JS::Zone* atomsZone;
JS::Zone** it;
JS::Zone** end;
public:
ZonesIter(JSRuntime* rt, ZoneSelector selector)
: iterMarker(&rt->gc),
atomsZone(selector == WithAtoms ? rt->gc.atomsZone.ref() : nullptr),
it(rt->gc.zones().begin()),
end(rt->gc.zones().end()) {
if (!atomsZone) {
skipHelperThreadZones();
}
}
bool done() const { return !atomsZone && it == end; }
void next() {
MOZ_ASSERT(!done());
if (atomsZone) {
atomsZone = nullptr;
} else {
it++;
}
skipHelperThreadZones();
}
void skipHelperThreadZones() {
while (!done() && get()->usedByHelperThread()) {
it++;
}
}
JS::Zone* get() const {
MOZ_ASSERT(!done());
return atomsZone ? atomsZone : *it;
}
operator JS::Zone*() const { return get(); }
JS::Zone* operator->() const { return get(); }
};
struct CompartmentsInZoneIter {
using ItemType = JS::Compartment;
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();
}
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(); }
};
class RealmsInZoneIter {
CompartmentsInZoneIter comp;
mozilla::Maybe<RealmsInCompartmentIter> realm;
public:
using ItemType = JS::Realm;
explicit RealmsInZoneIter(JS::Zone* zone) : comp(zone) {
settleOnCompartment();
}
void settleOnCompartment() {
if (!comp.done()) {
realm.emplace(comp.get());
MOZ_ASSERT(!realm->done(), "compartment must have at least one realm");
}
}
bool done() const {
MOZ_ASSERT(comp.done() == realm.isNothing());
return comp.done();
}
void next() {
MOZ_ASSERT(!done());
realm->next();
if (realm->done()) {
realm.reset();
comp.next();
settleOnCompartment();
}
}
JS::Realm* get() const { return realm->get(); }
operator JS::Realm*() const { return get(); }
JS::Realm* operator->() const { return get(); }
};
template <class ZonesIterT, class InnerIterT>
class CompartmentsOrRealmsIterT {
using T = typename InnerIterT::ItemType;
gc::AutoEnterIteration iterMarker;
ZonesIterT zone;
mozilla::Maybe<InnerIterT> inner;
public:
explicit CompartmentsOrRealmsIterT(JSRuntime* rt)
: iterMarker(&rt->gc), zone(rt, SkipAtoms) {
if (!zone.done()) {
inner.emplace(zone);
}
}
bool done() const { return zone.done(); }
void next() {
MOZ_ASSERT(!done());
MOZ_ASSERT(!inner.ref().done());
inner->next();
if (inner->done()) {
inner.reset();
zone.next();
if (!zone.done()) {
inner.emplace(zone);
}
}
}
T* get() const {
MOZ_ASSERT(!done());
return *inner;
}
operator T*() const { return get(); }
T* operator->() const { return get(); }
};
using CompartmentsIter =
CompartmentsOrRealmsIterT<ZonesIter, CompartmentsInZoneIter>;
using RealmsIter = CompartmentsOrRealmsIterT<ZonesIter, RealmsInZoneIter>;
}
#endif