tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

weak-marking-02.js (10423B)


      1 // |jit-test| allow-unhandlable-oom
      2 
      3 // These tests will be using object literals as keys, and we want some of them
      4 // to be dead after being inserted into a WeakMap. That means we must wrap
      5 // everything in functions because it seems like the toplevel script hangs onto
      6 // its object literals.
      7 
      8 // Cross-compartment WeakMap keys work by storing a cross-compartment wrapper
      9 // in the WeakMap, and the actual "delegate" object in the target compartment
     10 // is the thing whose liveness is checked.
     11 
     12 gczeal(0);
     13 
     14 var g2 = newGlobal({newCompartment: true});
     15 g2.eval('function genObj(name) { return {"name": name} }');
     16 
     17 function basicSweeping() {
     18  var wm1 = new WeakMap();
     19  wm1.set({'name': 'obj1'}, {'name': 'val1'});
     20  var hold = g2.genObj('obj2');
     21  wm1.set(hold, {'name': 'val2'});
     22  wm1.set({'name': 'obj3'}, {'name': 'val3'});
     23  var obj4 = g2.genObj('obj4');
     24  wm1.set(obj4, {'name': 'val3'});
     25  obj4 = undefined;
     26 
     27  startgc(100000, 'shrinking');
     28  gcslice();
     29  assertEq(wm1.get(hold).name, 'val2');
     30  assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
     31 }
     32 
     33 basicSweeping();
     34 
     35 // Same, but behind an additional WM layer, to avoid ordering problems (not
     36 // that I've checked that basicSweeping even has any problems.)
     37 
     38 function basicSweeping2() {
     39  var wm1 = new WeakMap();
     40  wm1.set({'name': 'obj1'}, {'name': 'val1'});
     41  var hold = g2.genObj('obj2');
     42  wm1.set(hold, {'name': 'val2'});
     43  wm1.set({'name': 'obj3'}, {'name': 'val3'});
     44  var obj4 = g2.genObj('obj4');
     45  wm1.set(obj4, {'name': 'val3'});
     46  obj4 = undefined;
     47 
     48  var base1 = {'name': 'base1'};
     49  var base2 = {'name': 'base2'};
     50  var wm_base1 = new WeakMap();
     51  var wm_base2 = new WeakMap();
     52  wm_base1.set(base1, wm_base2);
     53  wm_base2.set(base2, wm1);
     54  wm1 = wm_base2 = undefined;
     55 
     56  startgc(100000, 'shrinking');
     57  gcslice();
     58 
     59  assertEq(nondeterministicGetWeakMapKeys(wm_base1).length, 1);
     60  wm_base2 = wm_base1.get(base1);
     61  assertEq(nondeterministicGetWeakMapKeys(wm_base2).length, 1);
     62  assertEq(nondeterministicGetWeakMapKeys(wm_base1)[0], base1);
     63  assertEq(nondeterministicGetWeakMapKeys(wm_base2)[0], base2);
     64  wm_base2 = wm_base1.get(base1);
     65  wm1 = wm_base2.get(base2);
     66  assertEq(wm1.get(hold).name, 'val2');
     67  assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
     68 }
     69 
     70 basicSweeping2();
     71 
     72 // Scatter the weakmap, the keys, and the values among different compartments.
     73 
     74 function tripleZoneMarking() {
     75  var g1 = newGlobal({newCompartment: true});
     76  var g2 = newGlobal({newCompartment: true});
     77  var g3 = newGlobal({newCompartment: true});
     78 
     79  var wm = g1.eval("new WeakMap()");
     80  var key = g2.eval("({'name': 'obj1'})");
     81  var value = g3.eval("({'name': 'val1'})");
     82  g1 = g2 = g3 = undefined;
     83  wm.set(key, value);
     84 
     85  // Make all of it only reachable via a weakmap in the main test compartment,
     86  // so that all of this happens during weak marking mode. Use the weakmap as
     87  // its own key, so we know that the weakmap will get traced before the key
     88  // and therefore will populate the weakKeys table and all of that jazz.
     89  var base_wm = new WeakMap();
     90  base_wm.set(base_wm, [ wm, key ]);
     91 
     92  wm = key = value = undefined;
     93 
     94  startgc(100000, 'shrinking');
     95  gcslice();
     96 
     97  var keys = nondeterministicGetWeakMapKeys(base_wm);
     98  assertEq(keys.length, 1);
     99  var [ wm, key ] = base_wm.get(keys[0]);
    100  assertEq(key.name, "obj1");
    101  value = wm.get(key);
    102  assertEq(value.name, "val1");
    103 }
    104 
    105 tripleZoneMarking();
    106 
    107 // Same as above, but this time use enqueueMark to enforce ordering.
    108 
    109 function tripleZoneMarking2() {
    110  var g1 = newGlobal();
    111  var g2 = newGlobal();
    112  var g3 = newGlobal();
    113 
    114  var wm = g1.eval("wm = new WeakMap()");
    115  var key = g2.eval("key = ({'name': 'obj1'})");
    116  var value = g3.eval("({'name': 'val1'})");
    117  wm.set(key, value);
    118 
    119  enqueueMark("enter-weak-marking-mode");
    120  g1.eval("enqueueMark(wm)"); // weakmap
    121  g2.eval("enqueueMark(key)"); // delegate
    122  g1.wm = g2.key = undefined;
    123  g1 = g2 = g3 = undefined;
    124  wm = key = value = undefined;
    125 
    126  gc();
    127 
    128  var [ dummy, weakmap, keywrapper ] = getMarkQueue();
    129  assertEq(keywrapper.name, "obj1");
    130  value = weakmap.get(keywrapper);
    131  assertEq(value.name, "val1");
    132 
    133  clearMarkQueue();
    134 }
    135 
    136 if (this.enqueueMark)
    137  tripleZoneMarking2();
    138 
    139 function enbugger() {
    140  var g = newGlobal({newCompartment: true});
    141  var dbg = new Debugger;
    142  g.eval("function debuggee_f() { return 1; }");
    143  g.eval("function debuggee_g() { return 1; }");
    144  dbg.addDebuggee(g);
    145  var [ s ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_f");
    146  var [ s2 ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_g");
    147  g.eval("debuggee_f = null");
    148  gc();
    149  dbg.removeAllDebuggees();
    150  gc();
    151  assertEq(s.displayName, "debuggee_f");
    152 
    153  var wm = new WeakMap;
    154  var obj = Object.create(null);
    155  var obj2 = Object.create(null);
    156  wm.set(obj, s);
    157  wm.set(obj2, obj);
    158  wm.set(s2, obj2);
    159  s = s2 = obj = obj2 = null;
    160 
    161  gc();
    162 }
    163 
    164 enbugger();
    165 
    166 // Want to test: zone edges
    167 // Make map with cross-zone delegate. Collect the zones one at a time.
    168 function zone_edges() {
    169  var g3 = newGlobal();
    170  g3.eval('function genObj(name) { return {"name": name} }');
    171 
    172  var wm1 = new WeakMap();
    173  var hold = g2.genObj('obj1');
    174  var others = [g2.genObj('key2'), g2.genObj('key3')];
    175  wm1.set(hold, {'name': g3.genObj('val1'), 'others': others});
    176  others = null;
    177 
    178  var m = new Map;
    179  m.set(m, hold);
    180  hold = null;
    181 
    182  const zones = [ this, g2, g3 ];
    183  for (let zonebits = 0; zonebits < 2 ** zones.length; zonebits++) {
    184    for (let z in zones) {
    185      if (zonebits & (1 << z))
    186        schedulezone(zones[z]);
    187    }
    188    startgc(1);
    189    wm1.set(wm1.get(m.get(m)).others[0], g2.genObj('val2'));
    190    gcslice(1000000);
    191    wm1.set(wm1.get(m.get(m)).others[1], g2.genObj('val3'));
    192    gc();
    193    assertEq(wm1.get(m.get(m)).name.name, 'val1');
    194    assertEq(wm1.get(m.get(m)).others[0].name, 'key2');
    195    assertEq(wm1.get(wm1.get(m.get(m)).others[0]).name, 'val2');
    196    assertEq(wm1.get(m.get(m)).others[1].name, 'key3');
    197    assertEq(wm1.get(wm1.get(m.get(m)).others[1]).name, 'val3');
    198    assertEq(nondeterministicGetWeakMapKeys(wm1).length, 3);
    199  }
    200 
    201  // Do it again, with nuking.
    202  const wm2 = g2.eval("new WeakMap");
    203  wm2.set(wm1.get(m.get(m)).others[0], Object.create(null));
    204  for (let zonebits = 0; zonebits < 2 ** zones.length; zonebits++) {
    205    for (let z in zones) {
    206      if (zonebits & (1 << z))
    207        schedulezone(zones[z]);
    208    }
    209    startgc(1);
    210    wm1.set(wm1.get(m.get(m)).others[0], g2.genObj('val2'));
    211    gcslice(1000000);
    212    wm1.set(wm1.get(m.get(m)).others[1], g2.genObj('val3'));
    213    nukeCCW(wm1.get(wm1.get(m.get(m)).others[0]));
    214    nukeCCW(wm1.get(wm1.get(m.get(m)).others[1]));
    215    gc();
    216    assertEq(wm1.get(m.get(m)).name.name, 'val1');
    217    assertEq(wm1.get(m.get(m)).others[0].name, 'key2');
    218    assertEq(wm1.get(m.get(m)).others[1].name, 'key3');
    219    assertEq(nondeterministicGetWeakMapKeys(wm1).length, 3);
    220  }
    221 }
    222 
    223 zone_edges();
    224 
    225 // Stress test: lots of cross-zone and same-zone cross-compartment edges, and
    226 // exercise the barriers.
    227 function stress(opt) {
    228  printErr(JSON.stringify(opt));
    229 
    230  var g1 = this;
    231  var g2 = newGlobal({sameZoneAs: g1});
    232  var g3 = newGlobal();
    233 
    234  var globals = g1.globals = g2.globals = g3.globals = [ g1, g2, g3 ];
    235  g1.name = 'main';
    236  g2.name = 'same-zone';
    237  g3.name = 'other-zone';
    238  g1.names = g2.names = g3.names = [ g1.name, g2.name, g3.name ];
    239 
    240  // Basic setup:
    241  //
    242  // Three different globals, each with a weakmap and an object. Each global's
    243  // weakmap contains all 3 objects (1 from each global) as keys. Internally,
    244  // that means that each weakmap will contain one local object and two
    245  // cross-compartment wrappers.
    246  //
    247  // Now duplicate that 3 times. The first weakmap will be unmodified. The
    248  // second weakmap will have its keys updated to different values. The third
    249  // weakmap will have its keys deleted.
    250 
    251  for (const i in globals) {
    252    const g = globals[i];
    253    g.eval('function genObj(name) { return {"name": name} }');
    254    g.eval("weakmap0 = new WeakMap()");
    255    g.eval("weakmap1 = new WeakMap()");
    256    g.eval("weakmap2 = new WeakMap()");
    257    g.eval(`obj = genObj('global-${names[i]}-object')`);
    258    for (const j in [0, 1, 2]) {
    259      g.eval(`weakmap${j}.set(genObj('global-${names[i]}-key}'), genObj("value"))`);
    260    }
    261  }
    262 
    263  for (const i in globals) {
    264    const g = globals[i];
    265    for (const j in globals) {
    266      for (const k in [0, 1, 2]) {
    267        g.eval(`weakmap${k}.set(globals[${j}].obj, genObj('value-${i}-${j}'))`);
    268      }
    269    }
    270  }
    271 
    272  // Construct object keys to retrieve the weakmaps with.
    273  for (const g of globals) {
    274    g.eval(`plain = genObj("plain")`);
    275    g.eval(`update = genObj("update")`);
    276    g.eval(`remove = genObj("remove")`);
    277  }
    278 
    279  // Put the weakmaps in another WeakMap.
    280  for (const g of globals) {
    281      g.eval(`weakmaps = new WeakMap();
    282            weakmaps.set(plain, weakmap0);
    283            weakmaps.set(update, weakmap1);
    284            weakmaps.set(remove, weakmap2);`);
    285  }
    286 
    287  // Eliminate the edges from the global to the object being used as a key. But
    288  // assuming we want the key to be live (nothing else points to it), hide it
    289  // behind another weakmap layer.
    290  for (const g of globals) {
    291    if (opt.live) {
    292      g.eval("keyholder = genObj('key-holder')");
    293      g.eval("weakmaps.set(keyholder, obj)");
    294    }
    295    g.eval("obj = null");
    296  }
    297 
    298  // If we want a layer of indirection, remove the edges from the globals to
    299  // their weakmaps. But note that the original purpose of this test *wants*
    300  // the weakmaps themselves to be visited early, so that gcWeakKeys will be
    301  // populated with not-yet-marked keys and the barriers will need to update
    302  // entries there.
    303  if (opt.indirect) {
    304    for (const g of globals) {
    305      g.eval("weakmap0 = weakmap1 = weakmap2 = null");
    306    }
    307  }
    308 
    309  // Start an incremental GC. TODO: need a zeal mode to yield before entering
    310  // weak marking mode.
    311  startgc(1);
    312 
    313  // Do the mutations.
    314  if (opt.live) {
    315    for (const g of globals) {
    316      g.eval("weakmaps.get(update).set(weakmaps.get(keyholder), genObj('val'))");
    317      g.eval("weakmaps.get(remove).delete(weakmaps.get(keyholder))");
    318    }
    319  }
    320 
    321  if (opt.nuke) {
    322    for (const g of globals) {
    323      if (g.name != 'main')
    324        g.eval("nukeAllCCWs()");
    325    }
    326  }
    327 
    328  // Finish the GC.
    329  gc();
    330 }
    331 
    332 for (const live of [true, false]) {
    333  for (const indirect of [true, false]) {
    334    for (const nuke of [true, false]) {
    335      stress({live, indirect, nuke});
    336    }
    337  }
    338 }