Source code
Revision control
Copy as Markdown
Other Tools
// Test 'compartment revived' GCs, where we do an extra GC if there are
// compartments which we expected to die but were kept alive.
// A global used as the destination for transplants.
let transplantTargetGlobal = newGlobal();
function didCompartmentRevivedGC() {
return performance.mozMemory.gc.lastStartReason === "COMPARTMENT_REVIVED";
}
function compartmentCount() {
let r = performance.mozMemory.gc.compartmentCount;
return r;
}
function startIncrementalGC() {
startgc(1);
while (gcstate() === "Prepare" || gcstate() == "MarkRoots") {
gcslice(100, {dontStart: true});
}
assertEq(gcstate(), "Mark");
}
function finishIncrementalGC() {
while (gcstate() !== "NotActive") {
gcslice(100, {dontStart: true});
}
assertEq(gcstate(), "NotActive");
}
// Create a new compartment and global and return the global.
function createCompartment() {
return newGlobal({newCompartment: true});
}
// Create a transplantable object and create a wrapper to it from a new
// compartment. Return a function to transplant the target object.
function createTransplantableWrapperTarget(wrapperGlobal) {
let {object: target, transplant} = transplantableObject();
wrapperGlobal.wrapper = target;
return transplant;
}
// Transplant an object to a new global by calling the transplant
// function. This remaps all wrappers pointing to the target object,
// potentially keeping dead compartments alive.
function transplantTargetAndRemapWrappers(transplant) {
transplant(transplantTargetGlobal);
}
// Test no compartment revived GC triggered in normal cases.
function testNormal() {
gc();
assertEq(didCompartmentRevivedGC(), false);
startIncrementalGC();
finishIncrementalGC();
assertEq(didCompartmentRevivedGC(), false);
let initialCount = compartmentCount();
createCompartment();
startIncrementalGC();
finishIncrementalGC();
assertEq(compartmentCount(), initialCount);
}
// Test compartment revived GC is triggered by wrapper remapping.
function testCompartmentRevived1() {
let initialCount = compartmentCount();
let compartment = createCompartment();
let transplant = createTransplantableWrapperTarget(compartment);
compartment = null;
startIncrementalGC();
transplantTargetAndRemapWrappers(transplant);
finishIncrementalGC();
assertEq(didCompartmentRevivedGC(), true);
assertEq(compartmentCount(), initialCount);
}
// Test no compartment revived GC is triggered for compartments transitively
// kept alive by black roots.
function testCompartmentRevived2() {
let initialCount = compartmentCount();
let compartment = createCompartment();
let transplant = createTransplantableWrapperTarget(compartment);
let liveCompartment = createCompartment();
liveCompartment.wrapper = compartment;
compartment = null;
startIncrementalGC();
transplantTargetAndRemapWrappers(transplant);
finishIncrementalGC();
assertEq(didCompartmentRevivedGC(), false);
assertEq(compartmentCount(), initialCount + 2);
liveCompartment = null;
gc();
assertEq(compartmentCount(), initialCount);
}
// Test no compartment revived GC is triggered for compartments transitively
// kept alive by gray roots.
function testCompartmentRevived3() {
let initialCount = compartmentCount();
let compartment = createCompartment();
let transplant = createTransplantableWrapperTarget(compartment);
let liveCompartment = createCompartment();
liveCompartment.wrapper = compartment;
liveCompartment.eval('grayRoot()[0] = this');
liveCompartment = null;
gc();
startIncrementalGC();
transplantTargetAndRemapWrappers(transplant);
finishIncrementalGC();
assertEq(didCompartmentRevivedGC(), false);
assertEq(compartmentCount(), initialCount + 2);
// There's no easy way to clear gray roots for a compartment we don't have
// any reference to.
}
gczeal(0);
testNormal();
testCompartmentRevived1();
testCompartmentRevived2();
testCompartmentRevived3();