diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 4759a9331901..1bad50bcbd12 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1526,14 +1526,14 @@ static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) { static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - if (args.length() > 1) { + if (args.length() > 2) { RootedObject callee(cx, &args.callee()); ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); return false; } auto budget = SliceBudget::unlimited(); - if (args.length() == 1) { + if (args.length() >= 1) { uint32_t work = 0; if (!ToUint32(cx, args[0], &work)) { return false; @@ -1541,11 +1541,21 @@ static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) { budget = SliceBudget(WorkBudget(work)); } + bool dontStart = false; + if (args.get(1).isObject()) { + RootedObject options(cx, &args[1].toObject()); + RootedValue v(cx); + if (!JS_GetProperty(cx, options, "dontStart", &v)) { + return false; + } + dontStart = ToBoolean(v); + } + JSRuntime* rt = cx->runtime(); - if (!rt->gc.isIncrementalGCInProgress()) { - rt->gc.startDebugGC(GC_NORMAL, budget); - } else { + if (rt->gc.isIncrementalGCInProgress()) { rt->gc.debugGCSlice(budget); + } else if (!dontStart) { + rt->gc.startDebugGC(GC_NORMAL, budget); } args.rval().setUndefined(); @@ -6156,8 +6166,12 @@ gc::ZealModeHelpText), " Finish an in-progress incremental GC, if none is running then do nothing."), JS_FN_HELP("gcslice", GCSlice, 1, 0, -"gcslice([n])", -" Start or continue an an incremental GC, running a slice that processes about n objects."), +"gcslice([n [, options]])", +" Start or continue an an incremental GC, running a slice that processes\n" +" about n objects. Takes an optional options object, which may contain the\n" +" following properties:\n" +" dontStart: do not start a new incremental GC if one is not already\n" +" running"), JS_FN_HELP("abortgc", AbortGC, 1, 0, "abortgc()", @@ -6512,7 +6526,7 @@ gc::ZealModeHelpText), JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0, "getBacktrace([options])", " Return the current stack as a string. Takes an optional options object,\n" -" which may contain any or all of the boolean properties\n" +" which may contain any or all of the boolean properties:\n" " options.args - show arguments to each function\n" " options.locals - show local variables in each frame\n" " options.thisprops - show the properties of the 'this' object of each frame\n"), diff --git a/js/src/jit-test/tests/gc/bug-1643913.js b/js/src/jit-test/tests/gc/bug-1643913.js index 8717024eb342..3aa399f18c46 100644 --- a/js/src/jit-test/tests/gc/bug-1643913.js +++ b/js/src/jit-test/tests/gc/bug-1643913.js @@ -1,21 +1,36 @@ -// |jit-test| --enable-weak-refs; --no-ti +// |jit-test| --enable-weak-refs -for (let i = 70; i > 50; i--) { - gc(); - gczeal(10, i); +for (let p of [false, true]) { + f(p); - f(true, false, false); - f(true, false, true); + // Run an incremental GC to completion. + startgc(1); + while (gcstate() !== 'NotActive') { + gcslice(10000, { dontStart: true }); + } } function ccwToObject() { return evalcx('({})', newGlobal({newCompartment: true})); } -function f(x, y, z) { - let registry = new FinalizationRegistry(value => {}); - let target = x ? ccwToObject() : {}; - let heldValue = y ? ccwToObject() : {}; - let token = z ? ccwToObject() : {}; - registry.register(target, heldValue, token); +function ccwToRegistry() { + return evalcx('new FinalizationRegistry(value => {})', + newGlobal({newCompartment: true})); +} + +function f(p) { + let registry = ccwToRegistry(); + let target = ccwToObject(); + registry.register(target, undefined); + + // Add a CCW from registry to target zone or vice versa to control + // the order the zones are swept in. + if (p) { + registry.ptr = target; + } else { + target.ptr = registry; + } + + gc(); }