Bug 1746090 - Switch RootList::init from taking an AutoCheckCannotGC token to returning one r=jonco

Differential Revision: https://phabricator.services.mozilla.com/D135297
This commit is contained in:
Steve Fink 2022-01-20 00:23:10 +00:00
Родитель 2bd4ac2485
Коммит aea0c2fa07
8 изменённых файлов: 99 добавлений и 91 удалений

Просмотреть файл

@ -695,10 +695,13 @@ static bool AddGlobalsAsRoots(HandleObjectVector globals,
// If `boundaries` is incoherent, or we encounter an error while trying to
// handle it, or we run out of memory, set `rv` appropriately and return
// `false`.
static bool EstablishBoundaries(JSContext* cx, ErrorResult& rv,
const HeapSnapshotBoundaries& boundaries,
ubi::RootList& roots,
CompartmentSet& compartments) {
//
// Return value is a pair of the status and an AutoCheckCannotGC token,
// forwarded from ubi::RootList::init(), to ensure that the caller does
// not GC while the RootList is live and initialized.
static std::pair<bool, AutoCheckCannotGC> EstablishBoundaries(
JSContext* cx, ErrorResult& rv, const HeapSnapshotBoundaries& boundaries,
ubi::RootList& roots, CompartmentSet& compartments) {
MOZ_ASSERT(!roots.initialized());
MOZ_ASSERT(compartments.empty());
@ -709,48 +712,49 @@ static bool EstablishBoundaries(JSContext* cx, ErrorResult& rv,
if (!boundaries.mRuntime.Value()) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, AutoCheckCannotGC(cx)};
}
if (!roots.init()) {
auto [ok, nogc] = roots.init();
if (!ok) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return false;
return {false, nogc};
}
}
if (boundaries.mDebugger.WasPassed()) {
if (foundBoundaryProperty) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, AutoCheckCannotGC(cx)};
}
foundBoundaryProperty = true;
JSObject* dbgObj = boundaries.mDebugger.Value();
if (!dbgObj || !dbg::IsDebugger(*dbgObj)) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, AutoCheckCannotGC(cx)};
}
RootedObjectVector globals(cx);
if (!dbg::GetDebuggeeGlobals(cx, *dbgObj, &globals) ||
!PopulateCompartmentsWithGlobals(compartments, globals) ||
!roots.init(compartments) || !AddGlobalsAsRoots(globals, roots)) {
!roots.init(compartments).first || !AddGlobalsAsRoots(globals, roots)) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return false;
return {false, AutoCheckCannotGC(cx)};
}
}
if (boundaries.mGlobals.WasPassed()) {
if (foundBoundaryProperty) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, AutoCheckCannotGC(cx)};
}
foundBoundaryProperty = true;
uint32_t length = boundaries.mGlobals.Value().Length();
if (length == 0) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, AutoCheckCannotGC(cx)};
}
RootedObjectVector globals(cx);
@ -758,28 +762,29 @@ static bool EstablishBoundaries(JSContext* cx, ErrorResult& rv,
JSObject* global = boundaries.mGlobals.Value().ElementAt(i);
if (!JS_IsGlobalObject(global)) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, AutoCheckCannotGC(cx)};
}
if (!globals.append(global)) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return false;
return {false, AutoCheckCannotGC(cx)};
}
}
if (!PopulateCompartmentsWithGlobals(compartments, globals) ||
!roots.init(compartments) || !AddGlobalsAsRoots(globals, roots)) {
!roots.init(compartments).first || !AddGlobalsAsRoots(globals, roots)) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return false;
return {false, AutoCheckCannotGC(cx)};
}
}
AutoCheckCannotGC nogc(cx);
if (!foundBoundaryProperty) {
rv.Throw(NS_ERROR_INVALID_ARG);
return false;
return {false, nogc};
}
MOZ_ASSERT(roots.initialized());
return true;
return {true, nogc};
}
// A variant covering all the various two-byte strings that we can get from the
@ -1473,15 +1478,16 @@ void ChromeUtils::SaveHeapSnapshotShared(
JSContext* cx = global.Context();
{
Maybe<AutoCheckCannotGC> maybeNoGC;
ubi::RootList rootList(cx, maybeNoGC, wantNames);
if (!EstablishBoundaries(cx, rv, boundaries, rootList, compartments))
ubi::RootList rootList(cx, wantNames);
auto [ok, nogc] =
EstablishBoundaries(cx, rv, boundaries, rootList, compartments);
if (!ok) {
return;
}
StreamWriter writer(cx, gzipStream, wantNames,
!compartments.empty() ? &compartments : nullptr);
MOZ_ASSERT(maybeNoGC.isSome());
ubi::Node roots(&rootList);
// Serialize the initial heap snapshot metadata to the core dump.
@ -1489,10 +1495,10 @@ void ChromeUtils::SaveHeapSnapshotShared(
// Serialize the heap graph to the core dump, starting from our list of
// roots.
!WriteHeapGraph(cx, roots, writer, wantNames,
!compartments.empty() ? &compartments : nullptr,
maybeNoGC.ref(), nodeCount, edgeCount)) {
!compartments.empty() ? &compartments : nullptr, nogc,
nodeCount, edgeCount)) {
rv.Throw(zeroCopyStream.failed() ? zeroCopyStream.result()
: NS_ERROR_UNEXPECTED);
: NS_ERROR_UNEXPECTED);
return;
}
}

Просмотреть файл

@ -989,6 +989,7 @@ class JS_PUBLIC_API AutoRequireNoGC {
*/
class JS_PUBLIC_API AutoAssertNoGC : public AutoRequireNoGC {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
protected:
JSContext* cx_;
public:
@ -1063,13 +1064,19 @@ class JS_PUBLIC_API AutoAssertGCCallback : public AutoSuppressGCAnalysis {
class JS_PUBLIC_API AutoCheckCannotGC : public AutoAssertNoGC {
public:
explicit AutoCheckCannotGC(JSContext* cx = nullptr) : AutoAssertNoGC(cx) {}
} JS_HAZ_GC_INVALIDATED;
# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AutoCheckCannotGC(const AutoCheckCannotGC& other)
: AutoCheckCannotGC(other.cx_) {}
# else
AutoCheckCannotGC(const AutoCheckCannotGC& other) : AutoCheckCannotGC() {}
# endif
#else
class JS_PUBLIC_API AutoCheckCannotGC : public AutoRequireNoGC {
public:
explicit AutoCheckCannotGC(JSContext* cx = nullptr) {}
} JS_HAZ_GC_INVALIDATED;
class JS_PUBLIC_API AutoCheckCannotGC : public AutoRequireNoGC{
public :
explicit AutoCheckCannotGC(JSContext* cx = nullptr){} AutoCheckCannotGC(
const AutoCheckCannotGC& other) : AutoCheckCannotGC(){}
#endif
} JS_HAZ_GC_INVALIDATED;
extern JS_PUBLIC_API void SetLowMemoryState(JSContext* cx, bool newState);

Просмотреть файл

@ -967,50 +967,51 @@ class PreComputedEdgeRange : public EdgeRange {
//
// RootList::init itself causes a minor collection, but once the list of roots
// has been created, GC must not occur, as the referent ubi::Nodes are not
// stable across GC. The init calls emplace on |noGC|'s AutoCheckCannotGC, whose
// lifetime must extend at least as long as the RootList itself.
// stable across GC. It returns a [[nodiscard]] AutoCheckCannotGC token in order
// to enforce this. The token's lifetime must extend at least as long as the
// RootList itself. Note that the RootList does not itself contain a nogc field,
// which means that it is possible to store it somewhere that it can escape
// the init()'s nogc scope. Don't do that. (Or you could call some function
// and pass in the RootList and GC, but that would be caught.)
//
// Example usage:
//
// {
// mozilla::Maybe<JS::AutoCheckCannotGC> maybeNoGC;
// JS::ubi::RootList rootList(cx, maybeNoGC);
// if (!rootList.init()) {
// JS::ubi::RootList rootList(cx);
// auto [ok, nogc] = rootList.init();
// if (!ok()) {
// return false;
// }
//
// // The AutoCheckCannotGC is guaranteed to exist if init returned true.
// MOZ_ASSERT(maybeNoGC.isSome());
//
// JS::ubi::Node root(&rootList);
//
// ...
// }
class MOZ_STACK_CLASS JS_PUBLIC_API RootList {
Maybe<AutoCheckCannotGC>& noGC;
public:
JSContext* cx;
EdgeVector edges;
bool wantNames;
bool inited;
RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC,
bool wantNames = false);
explicit RootList(JSContext* cx, bool wantNames = false);
// Find all GC roots.
[[nodiscard]] bool init();
[[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init();
// Find only GC roots in the provided set of |JS::Compartment|s. Note: it's
// important to take a CompartmentSet and not a RealmSet: objects in
// same-compartment realms can reference each other directly, without going
// through CCWs, so if we used a RealmSet here we would miss edges.
[[nodiscard]] bool init(CompartmentSet& debuggees);
[[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init(
CompartmentSet& debuggees);
// Find only GC roots in the given Debugger object's set of debuggee
// compartments.
[[nodiscard]] bool init(HandleObject debuggees);
[[nodiscard]] std::pair<bool, JS::AutoCheckCannotGC> init(
HandleObject debuggees);
// Returns true if the RootList has been initialized successfully, false
// otherwise.
bool initialized() { return noGC.isSome(); }
bool initialized() { return inited; }
// Explicitly add the given Node as a root in this RootList. If wantNames is
// true, you must pass an edgeName. The RootList does not take ownership of

Просмотреть файл

@ -5712,21 +5712,20 @@ static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) {
Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
{
mozilla::Maybe<JS::AutoCheckCannotGC> maybeNoGC;
JS::ubi::Node root;
JS::ubi::RootList rootList(cx, maybeNoGC, true);
JS::ubi::RootList rootList(cx, true);
if (start.isNull()) {
if (!rootList.init()) {
auto [ok, nogc] = rootList.init();
if (!ok) {
ReportOutOfMemory(cx);
return false;
}
root = JS::ubi::Node(&rootList);
} else {
maybeNoGC.emplace(cx);
root = JS::ubi::Node(start);
}
JS::AutoCheckCannotGC& noGC = maybeNoGC.ref();
JS::AutoCheckCannotGC noGC(cx);
JS::ubi::NodeSet targets;

Просмотреть файл

@ -5779,15 +5779,15 @@ class MOZ_STACK_CLASS Debugger::ObjectQuery {
{
// We can't tolerate the GC moving things around while we're
// searching the heap. Check that nothing we do causes a GC.
Maybe<JS::AutoCheckCannotGC> maybeNoGC;
RootedObject dbgObj(cx, dbg->object);
JS::ubi::RootList rootList(cx, maybeNoGC);
if (!rootList.init(dbgObj)) {
JS::ubi::RootList rootList(cx);
auto [ok, nogc] = rootList.init(dbgObj);
if (!ok) {
ReportOutOfMemory(cx);
return false;
}
Traversal traversal(cx, *this, maybeNoGC.ref());
Traversal traversal(cx, *this, nogc);
traversal.wantNames = false;
return traversal.addStart(JS::ubi::Node(&rootList)) &&

Просмотреть файл

@ -413,14 +413,14 @@ bool DebuggerMemory::CallData::takeCensus() {
}
{
Maybe<JS::AutoCheckCannotGC> maybeNoGC;
JS::ubi::RootList rootList(cx, maybeNoGC);
if (!rootList.init(dbgObj)) {
JS::ubi::RootList rootList(cx);
auto [ok, nogc] = rootList.init(dbgObj);
if (!ok) {
ReportOutOfMemory(cx);
return false;
}
JS::ubi::CensusTraversal traversal(cx, handler, maybeNoGC.ref());
JS::ubi::CensusTraversal traversal(cx, handler, nogc);
traversal.wantNames = false;
if (!traversal.addStart(JS::ubi::Node(&rootList)) ||

Просмотреть файл

@ -361,38 +361,35 @@ const char16_t Concrete<js::RegExpShared>::concreteTypeName[] =
namespace JS {
namespace ubi {
RootList::RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC,
bool wantNames /* = false */)
: noGC(noGC), cx(cx), edges(), wantNames(wantNames) {}
RootList::RootList(JSContext* cx, bool wantNames /* = false */)
: cx(cx), edges(), wantNames(wantNames), inited(false) {}
bool RootList::init() {
std::pair<bool, JS::AutoCheckCannotGC> RootList::init() {
EdgeVectorTracer tracer(cx->runtime(), &edges, wantNames);
js::TraceRuntime(&tracer);
if (!tracer.okay) {
return false;
}
noGC.emplace();
return true;
inited = tracer.okay;
return {tracer.okay, JS::AutoCheckCannotGC(cx)};
}
bool RootList::init(CompartmentSet& debuggees) {
std::pair<bool, JS::AutoCheckCannotGC> RootList::init(
CompartmentSet& debuggees) {
EdgeVector allRootEdges;
EdgeVectorTracer tracer(cx->runtime(), &allRootEdges, wantNames);
ZoneSet debuggeeZones;
for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
if (!debuggeeZones.put(range.front()->zone())) {
return false;
return {false, JS::AutoCheckCannotGC(cx)};
}
}
js::TraceRuntime(&tracer);
if (!tracer.okay) {
return false;
return {false, JS::AutoCheckCannotGC(cx)};
}
js::gc::TraceIncomingCCWs(&tracer, debuggees);
if (!tracer.okay) {
return false;
return {false, JS::AutoCheckCannotGC(cx)};
}
for (EdgeVector::Range r = allRootEdges.all(); !r.empty(); r.popFront()) {
@ -409,15 +406,14 @@ bool RootList::init(CompartmentSet& debuggees) {
}
if (!edges.append(std::move(edge))) {
return false;
return {false, JS::AutoCheckCannotGC(cx)};
}
}
noGC.emplace();
return true;
return {true, JS::AutoCheckCannotGC(cx)};
}
bool RootList::init(HandleObject debuggees) {
std::pair<bool, JS::AutoCheckCannotGC> RootList::init(HandleObject debuggees) {
MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(*debuggees));
js::Debugger* dbg = js::Debugger::fromJSObject(debuggees.get());
@ -426,12 +422,13 @@ bool RootList::init(HandleObject debuggees) {
for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty();
r.popFront()) {
if (!debuggeeCompartments.put(r.front()->compartment())) {
return false;
return {false, JS::AutoCheckCannotGC(cx)};
}
}
if (!init(debuggeeCompartments)) {
return false;
auto [ok, nogc] = init(debuggeeCompartments);
if (!ok) {
return {false, nogc};
}
// Ensure that each of our debuggee globals are in the root list.
@ -439,15 +436,14 @@ bool RootList::init(HandleObject debuggees) {
r.popFront()) {
if (!addRoot(JS::ubi::Node(static_cast<JSObject*>(r.front())),
u"debuggee global")) {
return false;
return {false, nogc};
}
}
return true;
return {true, nogc};
}
bool RootList::addRoot(Node node, const char16_t* edgeName) {
MOZ_ASSERT(noGC.isSome());
MOZ_ASSERT_IF(wantNames, edgeName);
UniqueTwoByteChars name;

Просмотреть файл

@ -46,16 +46,15 @@ static void dumpNode(const JS::ubi::Node& node) {
JS_PUBLIC_API void dumpPaths(JSContext* cx, Node node,
uint32_t maxNumPaths /* = 10 */) {
mozilla::Maybe<AutoCheckCannotGC> nogc;
JS::ubi::RootList rootList(cx, nogc, true);
MOZ_ASSERT(rootList.init());
NodeSet targets;
bool ok = targets.putNew(node);
JS::ubi::RootList rootList(cx, true);
auto [ok, nogc] = rootList.init();
MOZ_ASSERT(ok);
auto paths = ShortestPaths::Create(cx, nogc.ref(), maxNumPaths, &rootList,
NodeSet targets;
ok = targets.putNew(node);
MOZ_ASSERT(ok);
auto paths = ShortestPaths::Create(cx, nogc, maxNumPaths, &rootList,
std::move(targets));
MOZ_ASSERT(paths.isSome());