diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index db9fd8c2ce69..61dcc4d8c157 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -393,10 +393,32 @@ class JS_PUBLIC_API(AutoAssertOnGC) }; /* - * Disable the static rooting hazard analysis in the live region, but assert if - * any GC occurs while this guard object is live. This is most useful to help - * the exact rooting hazard analysis in complex regions, since it cannot - * understand dataflow. + * Assert if an allocation of a GC thing occurs while this class is live. This + * class does not disable the static rooting hazard analysis. + */ +class JS_PUBLIC_API(AutoAssertNoAlloc) +{ +#ifdef JS_DEBUG + js::gc::GCRuntime *gc; + + public: + AutoAssertNoAlloc() : gc(nullptr) {} + explicit AutoAssertNoAlloc(JSRuntime *rt); + void disallowAlloc(JSRuntime *rt); + ~AutoAssertNoAlloc(); +#else + public: + AutoAssertNoAlloc() {} + explicit AutoAssertNoAlloc(JSRuntime *rt) {} + void disallowAlloc(JSRuntime *rt) {} +#endif +}; + +/* + * Disable the static rooting hazard analysis in the live region and assert if + * any allocation that could potentially trigger a GC occurs while this guard + * object is live. This is most useful to help the exact rooting hazard analysis + * in complex regions, since it cannot understand dataflow. * * Note: GC behavior is unpredictable even when deterministice and is generally * non-deterministic in practice. The fact that this guard has not @@ -406,11 +428,25 @@ class JS_PUBLIC_API(AutoAssertOnGC) * that the hazard analysis is correct for that code, rather than relying * on this class. */ -class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertOnGC +class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc { public: - AutoSuppressGCAnalysis() : AutoAssertOnGC() {} - explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertOnGC(rt) {} + AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {} + explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertNoAlloc(rt) {} +}; + +/* + * Assert that code is only ever called from a GC callback, disable the static + * rooting hazard analysis and assert if any allocation that could potentially + * trigger a GC occurs while this guard object is live. + * + * This is useful to make the static analysis ignore code that runs in GC + * callbacks. + */ +class JS_PUBLIC_API(AutoAssertGCCallback) : public AutoSuppressGCAnalysis +{ + public: + explicit AutoAssertGCCallback(JSObject *obj); }; /* diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 2349bbb8130f..cf5a7eca1ba5 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -222,6 +222,7 @@ function isRootedPointerTypeName(name) function isSuppressConstructor(name) { return name.indexOf("::AutoSuppressGC") != -1 + || name.indexOf("::AutoAssertGCCallback") != -1 || name.indexOf("::AutoEnterAnalysis") != -1 || name.indexOf("::AutoSuppressGCAnalysis") != -1 || name.indexOf("::AutoIgnoreRootingHazards") != -1; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 4a5542d0ce9a..4a252ffdb21b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -5642,12 +5642,12 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind, JS::gcreason::Reason reason) { /* GC shouldn't be running in parallel execution mode */ - MOZ_ASSERT(!InParallelSection()); + MOZ_ALWAYS_TRUE(!InParallelSection()); JS_AbortIfWrongThread(rt); /* If we attempt to invoke the GC while we are running in the GC, assert. */ - MOZ_ASSERT(!rt->isHeapBusy()); + MOZ_ALWAYS_TRUE(!rt->isHeapBusy()); /* The engine never locks across anything that could GC. */ MOZ_ASSERT(!rt->currentThreadHasExclusiveAccess()); @@ -6359,8 +6359,33 @@ JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt) if (rt->gc.isInsideUnsafeRegion()) MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region"); } + +JS::AutoAssertNoAlloc::AutoAssertNoAlloc(JSRuntime *rt) + : gc(nullptr) +{ + disallowAlloc(rt); +} + +void JS::AutoAssertNoAlloc::disallowAlloc(JSRuntime *rt) +{ + JS_ASSERT(!gc); + gc = &rt->gc; + gc->disallowAlloc(); +} + +JS::AutoAssertNoAlloc::~AutoAssertNoAlloc() +{ + if (gc) + gc->allowAlloc(); +} #endif +JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject *obj) + : AutoSuppressGCAnalysis() +{ + MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapMajorCollecting()); +} + #ifdef JSGC_HASH_TABLE_CHECKS void js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt) diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 3159a731e441..d0a3e4e9c8b4 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -325,37 +325,9 @@ class ZoneCellIterUnderGC : public ZoneCellIterImpl } }; -/* In debug builds, assert that no allocation occurs. */ -class AutoAssertNoAlloc -{ -#ifdef JS_DEBUG - GCRuntime *gc; - - public: - AutoAssertNoAlloc() : gc(nullptr) {} - explicit AutoAssertNoAlloc(JSRuntime *rt) : gc(nullptr) { - disallowAlloc(rt); - } - void disallowAlloc(JSRuntime *rt) { - JS_ASSERT(!gc); - gc = &rt->gc; - gc->disallowAlloc(); - } - ~AutoAssertNoAlloc() { - if (gc) - gc->allowAlloc(); - } -#else - public: - AutoAssertNoAlloc() {} - explicit AutoAssertNoAlloc(JSRuntime *) {} - void disallowAlloc(JSRuntime *rt) {} -#endif -}; - class ZoneCellIter : public ZoneCellIterImpl { - AutoAssertNoAlloc noAlloc; + JS::AutoAssertNoAlloc noAlloc; ArenaLists *lists; AllocKind kind;