summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/gc/compartment-revived-gc.js
blob: 881f4a622aa5fa2b71f383e394a794a354d2765c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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") {
    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();