tor-browser

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

dedupe-04.js (8111B)


      1 // Nursery dependent ND2 -> tenured dependent TD3 -> nursery base NB4 that is
      2 // deduplicated. The difficulty is that ND2 needs to update its chars pointer
      3 // because of the root base NB4 being deduplicated, but if TD3's whole cell
      4 // buffer entry is processed first, then it will no longer have a pointer to NB4
      5 // and cannot recover the original chars pointer.
      6 //
      7 // Note that this case as-is cannot happen in practice because it requires a
      8 // nursery -> tenured -> nursery dependent chain that would be very difficult to
      9 // create because most paths that create dependent strings will collapse base
     10 // chains and make the dependent string point directly at its root base. Rope
     11 // flattening is the one exception, but you'd still need to arrange for the
     12 // dependency chain to cross back and forth between the tenured heap and the
     13 // nursery.
     14 //
     15 // It is difficult to create a nursery -> tenured edge with string flattening,
     16 // because that would require creating a tenured rope with nursery children.
     17 // Given that the children must be created before the rope that points to them,
     18 // that means the rope creation would need to be pretenured or similar.
     19 
     20 // This case *does* happen in practice, in an actual web page. I'm not totally
     21 // clear on how. But it's actually the simpler case where no deduplication is
     22 // involved, just a nursery->tenured->nursery chain, which triggered an assert.
     23 function no_dedupe() {
     24    // Add an object to the whole cell buffer so it can be used later to trace
     25    // the correct string first. The whole cell buffer is used surprisingly
     26    // little outside the JIT and strings, but a tenured WeakRef with a nursery
     27    // target will use it. Fortunately, WeakRefs are always tenured.
     28    var Tobj = new WeakRef(Object.create(null));
     29 
     30    // Tenured -> nursery edge, put in whole cell buffer.
     31    Tobj.name = newString("blah", { tenured: false });
     32    var NB4 = newString("diddle doodle dawdle dink. piddle poodle paddle pink. widdle woodle wattle wink.", { tenured: false });
     33    var TD3 = newDependentString(NB4, 0, 70, { tenured: true });
     34    var ND2 = newDependentString(TD3, 0, 60, { tenured: false, 'suppress-contraction': true });
     35    Tobj.name = ND2; // Make Tobj point to ND2 to process it first.
     36    NB4 = TD3 = null;
     37 
     38    // When tracing the whole cell buffer, Tobj will cause ND2 to be promoted
     39    // first. Later ND2 is promoted, and it has a base chain of ND2->TD3->NB4.
     40    // TD3, though in the whole cell buffer, has not yet been traced, so it
     41    // still points to the nursery version of NB4. As a result, when ND2 is
     42    // promoted to TD2, it will see NB4 as its base string, which still has the
     43    // original chars pointer. ND2 is able to force-promote NB4 to a tenured TB4
     44    // and recompute its chars as TD2.chars=(NB4.chars-ND2.chars)+TB4.chars. It
     45    // is difficult but not impossible to trigger this code path in practice.
     46    minorgc();
     47    return ND2;
     48 }
     49 
     50 function with_dependent(mallocChars) {
     51    // Create a base NB5 to deduplicate to.
     52    var base = "MY YOUNGEST MEMORY IS OF A TOE, A GIANT BLUE TOE, IT MADE FUN OF ME INCESSANTLY BUT THAT DID NOT BOTHER ME IN THE LEAST. MY MOTHER WOULD HAVE BEEN HORRIFIED, BUT SHE WAS A GOOSE AND HAD ALREADY LAID THE EGG THAT CONTAINED ME SO SHE DID NOT ESPECIALLY CARE AND THOUGHT THAT IT WOULD BE GOOD TO BE TOUGHENED UP.";
     53    var NB5 = newString(base, { tenured: false });
     54 
     55    // Create a tenured dependent string early in the store buffer so NB5 is
     56    // promoted first and entered into the deduplication lookup table.
     57    var TD6 = newDependentString(NB5, 32, { tenured: true });
     58 
     59    // Create a base NB4 that will be deduplicated to TB5 aka tenured(NB5).
     60    var NB4 = newString(base, { tenured: false });
     61 
     62    // Create a tenured dependent string that will be processed next, and lose its
     63    // pointer to the original nursery base NB4. Which is fine for itself since it
     64    // can update its chars pointer before losing NB4, but any dependencies
     65    // processed later will no longer have a way of knowing their original base.
     66    var TD3 = newDependentString(NB4, 25, { tenured: true });
     67 
     68    // A nursery dependent string to get messed up.
     69    Math.cos(0);
     70    var ND2 = newDependentString(TD3, 0, 81, { tenured: false, 'suppress-contraction': true });
     71 
     72    // For debuggging:
     73    Math.sin(0, "ND2", ND2, "TD3", TD3, "NB4", NB4, "NB5", NB5, "TD6", TD6);
     74 
     75    // Clear some roots.
     76    TD3 = NB4 = NB5 = "";
     77 
     78    var preGC_ND2_rep = this.stringRepresentation ? JSON.parse(stringRepresentation(ND2)) : null;
     79    gc();
     80    assertEq(ND2, "A TOE, A GIANT BLUE TOE, IT MADE FUN OF ME INCESSANTLY BUT THAT DID NOT BOTHER ME");
     81 }
     82 
     83 function with_rope() {
     84    // Create a base NB5 to deduplicate to.
     85    var base = "MY YOUNGEST MEMORY IS OF A TOE, A GIANT BLUE TOE, IT MADE FUN OF ME INCESSANTLY BUT THAT DID NOT BOTHER ME IN THE LEAST. MY MOTHER WOULD HAVE BEEN HORRIFIED, BUT SHE WAS A GOOSE AND HAD ALREADY LAID THE EGG THAT CONTAINED ME SO SHE DID NOT ESPECIALLY CARE AND THOUGHT THAT IT WOULD BE GOOD TO BE TOUGHENED UP.";
     86    var NB5 = newString(base, { tenured: false, capacity: 400 });
     87 
     88    // Create a tenured dependent string early in the store buffer so NB5 is
     89    // promoted first and entered into the deduplication lookup table.
     90    var TD6 = newDependentString(NB5, 32, { tenured: true });
     91 
     92    // Create a tenured dependent string that will be processed next, and lose
     93    // its pointer to the original nursery base NB4. Which is fine for itself
     94    // since it can update its chars pointer before losing NB4, but any
     95    // dependencies processed later will no longer have a way of knowing their
     96    // original base.
     97    //
     98    // Initially, this is an extensible string with excess capacity. It will be
     99    // converted a dependent string during flattening, below.
    100    var TD3 = newString("MY YOUNGEST MEMORY IS OF A TOE, A GIANT BLUE TOE, IT MADE FUN OF ME INCESSANTLY BUT THAT DID NOT BOTHER ME IN THE LEAST. MY MOTHER WOULD HAVE BEEN HORRIFIED, BUT ",
    101        { tenured: true, capacity: 1000 });
    102 
    103    // A nursery dependent string to get messed up.
    104    var ND2 = newDependentString(TD3, 25, 106, { tenured: false });
    105 
    106    // Flatten a rope to convert TD3 from extensible to dependent.
    107    var suffix = "SHE WAS A GOOSE AND HAD ALREADY LAID THE EGG THAT CONTAINED ME SO SHE DID NOT ESPECIALLY CARE AND THOUGHT THAT IT WOULD BE GOOD TO BE TOUGHENED UP.";
    108 
    109    // Create a base NB4 that will be deduplicated to TB5 aka tenured(NB5).
    110    var NB4 = TD3 + suffix;
    111    ensureLinearString(NB4);
    112 
    113    // For debuggging:
    114    Math.sin(0, "ND2", ND2, "TD3", TD3, "NB4", NB4, "NB5", NB5, "TD6", TD6);
    115 
    116    var NB4_rep = this.stringRepresentation ? JSON.parse(stringRepresentation(NB4)) : null;
    117 
    118    // Clear some roots.
    119    rope = suffix = TD3 = NB4 = NB5 = "";
    120 
    121    gc();
    122    print(ND2);
    123    assertEq(ND2, "A TOE, A GIANT BLUE TOE, IT MADE FUN OF ME INCESSANTLY BUT THAT DID NOT BOTHER ME");
    124    // This only works because NB4 has the NON_DEDUP_BIT, and is an extensible
    125    // string so its data will not be allocated from the nursery.
    126    if (NB4_rep !== null) {
    127        assertEq(NB4_rep.flags.includes("NON_DEDUP_BIT"), true);
    128    }
    129 }
    130 
    131 function atomref() {
    132    // Create a string too big to be inline if stored twoByte, but small enough
    133    // if stored latin1, and force it to be twoByte.
    134    var inlineIfLatin1 = newString("0123456789", { tenured: false, twoByte: true });
    135 
    136    // Make a tenured rope with a nursery child so it goes into the whole cell
    137    // buffer.
    138    var s = newRope(inlineIfLatin1, "abc", { nursery: false });
    139 
    140    // Make a toplevel rope node for flattening fun.
    141    var rope = newRope("....", s);
    142 
    143    // Flatten the toplevel, so `s` becomes a dependent string in the whole cell
    144    // buffer, pointing at the root.
    145    ensureLinearString(rope);
    146 
    147    // Convert the string to an AtomRef to an atom, deflated to latin1 so it is
    148    // inline.
    149    ({})[s] = true;
    150 
    151    // The whole cell buffer will trace `s`, which is an atomref (a type of
    152    // dependent string) that points to an inline atom.
    153    minorgc();
    154 }
    155 
    156 with_dependent();
    157 with_rope();
    158 atomref();