зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2bd4ac2485
Коммит
aea0c2fa07
|
@ -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());
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче