diff --git a/js/src/jsapi.c b/js/src/jsapi.c index c2f63e78bef7..423f2174c93a 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -3643,7 +3643,8 @@ JS_SaveExceptionState(JSContext *cx) if (state) { state->throwing = JS_GetPendingException(cx, &state->exception); if (state->throwing && JSVAL_IS_GCTHING(state->exception)) - JS_AddRoot(cx, &state->exception); + js_AddRoot(cx, &state->exception, + "JSExceptionState.exception"); } return state; #else diff --git a/js/src/jscntxt.c b/js/src/jscntxt.c index 17ba9dd6911b..f4b9d1c0e668 100644 --- a/js/src/jscntxt.c +++ b/js/src/jscntxt.c @@ -146,6 +146,21 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) return cx; } +#if DEBUG +JS_STATIC_DLL_CALLBACK(intN) +js_root_printer(JSHashEntry *he, intN i, void *arg) +{ + uint32 *leakedroots = (uint32 *)arg; + + *leakedroots += 1; + fprintf(stderr, + "JS engine warning: leaking GC root \'%s\' at location %p\n", + he->value ? (char *)he->value : "", he->key); + + return HT_ENUMERATE_NEXT; +} +#endif + void js_DestroyContext(JSContext *cx, JSGCMode gcmode) { @@ -199,6 +214,10 @@ js_DestroyContext(JSContext *cx, JSGCMode gcmode) #endif if (last) { +#if DEBUG + uint32 leakedroots = 0; +#endif + /* Always force, so we wait for any racing GC to finish. */ js_ForceGC(cx); @@ -206,6 +225,26 @@ js_DestroyContext(JSContext *cx, JSGCMode gcmode) while (rt->gcPoke) js_GC(cx, GC_LAST_CONTEXT); +#if DEBUG + /* Warn (but don't assert) debug builds of any remaining roots. */ + JS_HashTableEnumerateEntries(rt->gcRootsHash, js_root_printer, + &leakedroots); + if (leakedroots > 0) { + if (leakedroots == 1) { + fprintf(stderr, +"JS engine warning: 1 GC root remains after destroying the last JSContext.\n" +" This root may point to freed memory, and objects reachable\n" +" through it have not been finalized.\n"); + } else { + fprintf(stderr, +"JS engine warning: %lu GC roots remain after destroying the last JSContext.\n" +" These roots may point to freed memory, and objects reachable\n" +" through them have not been finalized.\n", + (unsigned long) leakedroots); + } + } +#endif + /* Free atom state last, now that no scripts survive. */ js_FreeAtomState(cx, &rt->atomState);