Source code

Revision control

Other Tools

/* -*- 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