Bug 1655203 - Add tests for AutoStableStringChars and deduplication r=jonco
☠☠ backed out by 3c95d3e585f3 ☠ ☠
authorSteve Fink <sfink@mozilla.com>
Tue, 28 Jul 2020 04:37:27 +0000
changeset 542281 d2de3e4125b30fc0b7f67cb489b9f44690795c89
parent 542280 0d918902af47e02efbe37253d32a0e0292338c2e
child 542282 3c95d3e585f33f60a2b4de9ef5d68e990ec08074
push id37645
push userccoroiu@mozilla.com
push dateTue, 28 Jul 2020 09:47:25 +0000
treeherdermozilla-central@eba7e3ce9382 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1655203
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1655203 - Add tests for AutoStableStringChars and deduplication r=jonco Differential Revision: https://phabricator.services.mozilla.com/D84872
js/src/jit-test/tests/gc/dedupe-02.js
js/src/jit-test/tests/gc/dedupe.js
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testDeduplication.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/dedupe-02.js
@@ -0,0 +1,56 @@
+// AutoStableStringChars needs to prevent the owner of its chars from being
+// deduplicated, even if they are held by a different string.
+
+gczeal(0);
+
+function makeExtensibleStrFrom(str) {
+  var left = str.substr(0, str.length/2);
+  var right = str.substr(str.length/2, str.length);
+  var ropeStr = left + right;
+  return ensureLinearString(ropeStr);
+}
+
+// Make a string to deduplicate to.
+var original = makeExtensibleStrFrom('{ "phbbbbbbbbbbbbbbttt!!!!??": [1] }\n\n');
+
+// Construct D2 -> D1 -> base
+var D2 = makeExtensibleStrFrom('{ "phbbbbbbbbbbbbbbttt!!!!??": [1] }');
+var D1 = newRope(D2, '\n', {nursery: true});
+ensureLinearString(D1);
+var base = newRope(D1, '\n', {nursery: true});
+ensureLinearString(base);
+
+dumpStringRepresentation(D2);
+print("----");
+dumpStringRepresentation(D1);
+print("----");
+dumpStringRepresentation(base);
+print("----");
+dumpStringRepresentation(original);
+
+// Make an AutoStableStringChars(D2) and do a minor GC within it. (This will do
+// a major GC, but it'll start out with a minor GC.) `base` would get
+// deduplicated to `original`, if it weren't for AutoStableStringChars marking
+// all of D2, D1, and base non-deduplicatable.
+
+// The first time JSON.parse runs, it will create several (14 in my test) GC
+// things before getting to the point where it does an allocation while holding
+// the chars pointer. Get them out of the way now.
+JSON.parse(D2);
+
+// Cause a minor GC to happen during JSON.parse after AutoStableStringChars
+// gives up its pointer.
+schedulegc(1);
+JSON.parse(D2);
+
+// Access `D2` to verify that it is not using the deduplicated chars.
+print(D2);
+
+print("\n\n<---->\n\n");
+dumpStringRepresentation(D2);
+print("----");
+dumpStringRepresentation(D1);
+print("----");
+dumpStringRepresentation(base);
+print("----");
+dumpStringRepresentation(original);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/dedupe.js
@@ -0,0 +1,44 @@
+function str(c) {
+    let s = c;
+    for (let i = 0; i < 30; i++)
+        s += c;
+    ensureLinearString(s);
+    return s;
+}
+
+function f() {
+  // Create some slots to write into.
+  const o = {owner: 'short1', s: 'short2'};
+
+  // Make a tenured rope.
+  const r1 = str("a") + str("b");
+  gc();
+
+  // Write the first instance of our duplicate string into one of the slots
+  // (`owner`). This will be scanned first, and entered into deDupSet when
+  // tenured.
+  o.owner = ensureLinearString(str("a") + str("b") + str("c"));
+
+  // Make another rope with identical contents, with a tenured subtree.
+  const r2 = r1 + str("c");
+
+  // Linearize the new rope, creating a new extensible string and a bunch of
+  // dependent strings replacing the rest of the rope nodes.
+  ensureLinearString(r2);
+
+  // Write the new rope into a slot, so that it will be scanned next during the
+  // minor GC during traceSlots().
+  o.s = r2;
+
+  // Do a nursery collection. o.owner will be tenured and inserted into
+  // deDupSet. Then o.s aka r2 will be tenured. If things work correctly, r2
+  // will be marked non-deduplicatable because it is the base of a tenured
+  // string r1. If not, it will be deduplicated to o.owner.
+  minorgc();
+
+  // Extract out that r1 child node. If its base was deduplicated, this will
+  // assert because its chars have been freed.
+  const s1 = r1.substr(0, 31);
+}
+
+f();
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -23,16 +23,17 @@ UNIFIED_SOURCES += [
     'testBug604087.cpp',
     'testCallArgs.cpp',
     'testCallNonGenericMethodOnProxy.cpp',
     'testChromeBuffer.cpp',
     'testCompileNonSyntactic.cpp',
     'testCompileUtf8.cpp',
     'testDateToLocaleString.cpp',
     'testDebugger.cpp',
+    'testDeduplication.cpp',
     'testDeepFreeze.cpp',
     'testDefineGetterSetterNonEnumerable.cpp',
     'testDefineProperty.cpp',
     'testDefinePropertyIgnoredAttributes.cpp',
     'testDeflateStringToUTF8Buffer.cpp',
     'testDifferentNewTargetInvokeConstructor.cpp',
     'testEmptyWindowIsOmitted.cpp',
     'testErrorCopying.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testDeduplication.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <string.h>
+
+#include "jsfriendapi.h"
+
+#include "js/RootingAPI.h"
+#include "js/StableStringChars.h"
+
+#include "jsapi-tests/tests.h"
+
+#include "vm/JSContext.h"
+#include "vm/StringType.h"
+
+#include "vm/JSContext-inl.h"
+
+static bool SameChars(JSContext* cx, JSString* str1, JSString* str2,
+                      size_t offset) {
+  JS::AutoCheckCannotGC nogc(cx);
+
+  const JS::Latin1Char* chars1 =
+      js::StringToLinearString(cx, str1)->latin1Chars(nogc);
+  const JS::Latin1Char* chars2 =
+      js::StringToLinearString(cx, str2)->latin1Chars(nogc);
+
+  return chars1 == chars2 + offset;
+}
+
+BEGIN_TEST(testDeduplication_ASSC) {
+  // Test with a long enough string to avoid inline chars allocation.
+  const char text[] =
+      "Andthebeastshallcomeforthsurroundedbyaroilingcloudofvengeance."
+      "Thehouseoftheunbelieversshallberazedandtheyshallbescorchedtoth"
+      "eearth.Theirtagsshallblinkuntiltheendofdays.";
+
+  // Create a string to deduplicate later strings to.
+  JS::RootedString original(cx, JS_NewStringCopyZ(cx, text));
+  CHECK(original);
+
+  // Create a chain of dependent strings, with a base string whose contents
+  // match `original`'s.
+  JS::RootedString str(cx, JS_NewStringCopyZ(cx, text));
+  CHECK(str);
+
+  JS::RootedString dep(cx, JS_NewDependentString(cx, str, 10, 100));
+  CHECK(str);
+
+  JS::RootedString depdep(cx, JS_NewDependentString(cx, dep, 10, 80));
+  CHECK(str);
+
+  // Repeat. This one will not be prevented from deduplication.
+  JS::RootedString str2(cx, JS_NewStringCopyZ(cx, text));
+  CHECK(str);
+
+  JS::RootedString dep2(cx, JS_NewDependentString(cx, str2, 10, 100));
+  CHECK(str);
+
+  JS::RootedString depdep2(cx, JS_NewDependentString(cx, dep2, 10, 80));
+  CHECK(str);
+
+  // Initializing an AutoStableStringChars with `depdep` should prevent the
+  // owner of its chars (`str`) from deduplication.
+  JS::AutoStableStringChars stable(cx);
+  CHECK(stable.init(cx, depdep));
+
+  const JS::Latin1Char* chars = stable.latin1Chars();
+  CHECK(memcmp(chars, text + 20, 80 * sizeof(JS::Latin1Char)) == 0);
+
+  // `depdep` should share chars with `str` but not with `original`.
+  CHECK(SameChars(cx, depdep, str, 20));
+  CHECK(!SameChars(cx, depdep, original, 20));
+
+  // Same for `depdep2`.
+  CHECK(SameChars(cx, depdep2, str2, 20));
+  CHECK(!SameChars(cx, depdep2, original, 20));
+
+  // Do a minor GC that will deduplicate `str2` to `original`, and would have
+  // deduplicated `str` as well if it weren't prevented by the
+  // AutoStableStringChars.
+  cx->minorGC(JS::GCReason::API);
+
+  // `depdep` should still share chars with `str` but not with `original`.
+  CHECK(SameChars(cx, depdep, str, 20));
+  CHECK(!SameChars(cx, depdep, original, 20));
+
+  // `depdep2` should now share chars with both `str` and `original`.
+  CHECK(SameChars(cx, depdep2, str2, 20));
+  CHECK(SameChars(cx, depdep2, original, 20));
+
+  return true;
+}
+END_TEST(testDeduplication_ASSC)