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();