summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/heap-analysis
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/heap-analysis')
-rw-r--r--js/src/jit-test/tests/heap-analysis/bug-1249107.js1
-rw-r--r--js/src/jit-test/tests/heap-analysis/bug-1252912.js6
-rw-r--r--js/src/jit-test/tests/heap-analysis/bug-1254105.js3
-rw-r--r--js/src/jit-test/tests/heap-analysis/byteSize-of-object.js83
-rw-r--r--js/src/jit-test/tests/heap-analysis/byteSize-of-scripts.js46
-rw-r--r--js/src/jit-test/tests/heap-analysis/byteSize-of-string.js133
-rw-r--r--js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js25
-rw-r--r--js/src/jit-test/tests/heap-analysis/findPath.js48
-rw-r--r--js/src/jit-test/tests/heap-analysis/pointerByteSize.js3
-rw-r--r--js/src/jit-test/tests/heap-analysis/shortestPaths.js44
10 files changed, 392 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/heap-analysis/bug-1249107.js b/js/src/jit-test/tests/heap-analysis/bug-1249107.js
new file mode 100644
index 000000000..81792398b
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/bug-1249107.js
@@ -0,0 +1 @@
+shortestPaths(this, [this], 5)
diff --git a/js/src/jit-test/tests/heap-analysis/bug-1252912.js b/js/src/jit-test/tests/heap-analysis/bug-1252912.js
new file mode 100644
index 000000000..1240b4545
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/bug-1252912.js
@@ -0,0 +1,6 @@
+try {
+ x = evalcx('')
+ toSource = (function() {
+ })
+} catch (foo) {}
+shortestPaths(this, ["$4"], 5)
diff --git a/js/src/jit-test/tests/heap-analysis/bug-1254105.js b/js/src/jit-test/tests/heap-analysis/bug-1254105.js
new file mode 100644
index 000000000..5d6ff8ba6
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/bug-1254105.js
@@ -0,0 +1,3 @@
+// |jit-test| error:Error: Each target must be an object, string, or symbol
+
+shortestPaths(this, [, , , undefined], 5)
diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
new file mode 100644
index 000000000..3b667aa4a
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
@@ -0,0 +1,83 @@
+// Check that JS::ubi::Node::size returns reasonable results for objects.
+
+// We actually hard-code specific sizes into this test, even though they're
+// implementation details, because in practice there are only two architecture
+// variants to consider (32-bit and 64-bit), and if these sizes change, that's
+// something SpiderMonkey hackers really want to know; they're supposed to be
+// stable.
+
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+if (!getBuildConfiguration()['moz-memory'])
+ quit(0);
+
+if (getBuildConfiguration()['pointer-byte-size'] == 4)
+ var s = (s32, s64) => s32
+else
+ var s = (s32, s64) => s64
+
+function tenure(obj) {
+ gc();
+ return obj;
+}
+
+// Return the byte size of |obj|, ensuring that the size is not affected by
+// being tenured. (We use 'survives a GC' as an approximation for 'tenuring'.)
+function tByteSize(obj) {
+ var size = byteSize(obj);
+ minorgc();
+ if (size != byteSize(obj))
+ return 0;
+ return size;
+}
+
+assertEq(tByteSize({}), s(16, 32));
+
+// Try objects with only named properties.
+assertEq(tByteSize({ w: 1 }), s(32, 48));
+assertEq(tByteSize({ w: 1, x: 2 }), s(32, 48));
+assertEq(tByteSize({ w: 1, x: 2, y: 3 }), s(48, 64));
+assertEq(tByteSize({ w: 1, x: 2, y: 3, z:4 }), s(48, 64));
+assertEq(tByteSize({ w: 1, x: 2, y: 3, z:4, a: 5 }), s(80, 96));
+
+// Try objects with only indexed properties.
+assertEq(tByteSize({ 0:0 }), s(96, 112));
+assertEq(tByteSize({ 0:0, 1:1 }), s(96, 112));
+assertEq(tByteSize({ 0:0, 1:1, 2:2 }), s(112, 128));
+assertEq(tByteSize({ 0:0, 1:1, 2:2, 3:3 }), s(112, 128));
+assertEq(tByteSize({ 0:0, 1:1, 2:2, 3:3, 4:4 }), s(144, 160));
+
+// Mix indexed and named properties, exploring each combination of the size
+// classes above.
+//
+// Oddly, the changes here as the objects grow are not simply the sums of the
+// changes above: for example, with one named property, the objects with three
+// and five indexed properties are in different size classes; but with three
+// named properties, there's no break there.
+assertEq(tByteSize({ w:1, 0:0 }), s(96, 112));
+assertEq(tByteSize({ w:1, 0:0, 1:1, 2:2 }), s(112, 128));
+assertEq(tByteSize({ w:1, 0:0, 1:1, 2:2, 3:3, 4:4 }), s(144, 160));
+assertEq(tByteSize({ w:1, x:2, y:3, 0:0 }), s(112, 128));
+assertEq(tByteSize({ w:1, x:2, y:3, 0:0, 1:1, 2:2 }), s(144, 160));
+assertEq(tByteSize({ w:1, x:2, y:3, 0:0, 1:1, 2:2, 3:3, 4:4 }), s(144, 160));
+assertEq(tByteSize({ w:1, x:2, y:3, z:4, a:6, 0:0 }), s(144, 160));
+assertEq(tByteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2 }), s(144, 160));
+assertEq(tByteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2, 3:3, 4:4 }), s(176, 192));
+
+// Check various lengths of array.
+assertEq(tByteSize([]), s(80, 96));
+assertEq(tByteSize([1]), s(48, 64));
+assertEq(tByteSize([1, 2]), s(48, 64));
+assertEq(tByteSize([1, 2, 3]), s(80, 96));
+assertEq(tByteSize([1, 2, 3, 4]), s(80, 96));
+assertEq(tByteSize([1, 2, 3, 4, 5]), s(80, 96));
+assertEq(tByteSize([1, 2, 3, 4, 5, 6]), s(80, 96));
+assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7]), s(112, 128));
+assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7, 8]), s(112, 128));
+
+// Various forms of functions.
+assertEq(tByteSize(function () {}), s(32, 64));
+assertEq(tByteSize(function () {}.bind()), s(48, 80));
+assertEq(tByteSize(() => 1), s(48, 80));
+assertEq(tByteSize(Math.sin), s(32, 64));
diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-scripts.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-scripts.js
new file mode 100644
index 000000000..11923b35b
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-scripts.js
@@ -0,0 +1,46 @@
+// Check JS::ubi::Node::size results for scripts. We don't attempt to check
+// exact sizes in this test (deemed to difficult and non-deterministic), just
+// some sanity checks.
+
+function f1() {
+ return 42;
+}
+
+print("byteSizeOfScript(f1) = " + byteSizeOfScript(f1));
+assertEq(byteSizeOfScript(f1) > 1, true);
+
+function f2(n) {
+ var obj = {
+ x: 1,
+ y: 2,
+ z: 3,
+ };
+
+ if (i % 2 == 0) {
+ for (var i = 0; i < n; i++) {
+ this.x += i;
+ print(uneval(i));
+ obj[i] = i * i;
+ if (i > 10) {
+ f2(i / f1());
+ }
+ }
+ }
+
+ if (i % 3 == 0) {
+ for (var i = 0; i < n; i++) {
+ this.x *= i;
+ print(uneval(i));
+ obj[i] = i * i;
+ if (i > 10) {
+ f2(i / f1());
+ }
+ }
+ }
+
+ return this.x;
+}
+
+print("byteSizeOfScript(f2) = " + byteSizeOfScript(f2));
+assertEq(byteSizeOfScript(f2) > 1, true);
+assertEq(byteSizeOfScript(f2) > byteSizeOfScript(f1), true);
diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
new file mode 100644
index 000000000..251d30d86
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
@@ -0,0 +1,133 @@
+// Check JS::ubi::Node::size results for strings.
+
+// We actually hard-code specific sizes into this test, even though they're
+// implementation details, because in practice there are only two architecture
+// variants to consider (32-bit and 64-bit), and if these sizes change, that's
+// something SpiderMonkey hackers really want to know; they're supposed to be
+// stable.
+
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+var config = getBuildConfiguration();
+if (!config['moz-memory'])
+ quit(0);
+
+if (config['pointer-byte-size'] == 4)
+ var s = (s32, s64) => s32
+else
+ var s = (s32, s64) => s64
+
+// Return the byte size of |obj|, ensuring that the size is not affected by
+// being tenured. (We use 'survives a GC' as an approximation for 'tenuring'.)
+function tByteSize(obj) {
+ var nurserySize = byteSize(obj);
+ minorgc();
+ var tenuredSize = byteSize(obj);
+ if (nurserySize != tenuredSize) {
+ print("nursery size: " + nurserySize + " tenured size: " + tenuredSize);
+ return -1; // make the stack trace point at the real test
+ }
+
+ return tenuredSize;
+}
+
+// There are four representations of flat strings, with the following capacities
+// (excluding a terminating null character):
+//
+// 32-bit 64-bit test
+// representation Latin-1 char16_t Latin-1 char16_t label
+// ========================================================================
+// JSExternalString (cannot be tested in shell) -
+// JSThinInlineString 7 3 15 7 T
+// JSFatInlineString 23 11 23 11 F
+// JSExtensibleString - limited by available memory - X
+// JSUndependedString - same as JSExtensibleString -
+
+// Note that atoms are 8 bytes larger than non-atoms, to store the atom's hash code.
+
+// Latin-1
+assertEq(tByteSize(""), s(24, 32)); // T, T
+assertEq(tByteSize("1"), s(24, 32)); // T, T
+assertEq(tByteSize("1234567"), s(24, 32)); // T, T
+assertEq(tByteSize("12345678"), s(40, 32)); // F, T
+assertEq(tByteSize("123456789.12345"), s(40, 32)); // F, T
+assertEq(tByteSize("123456789.123456"), s(40, 40)); // F, F
+assertEq(tByteSize("123456789.123456789.123"), s(40, 40)); // F, F
+assertEq(tByteSize("123456789.123456789.1234"), s(56, 64)); // X, X
+assertEq(tByteSize("123456789.123456789.123456789.1"), s(56, 64)); // X, X
+assertEq(tByteSize("123456789.123456789.123456789.12"), s(72, 80)); // X, X
+
+// Inline char16_t atoms.
+// "Impassionate gods have never seen the red that is the Tatsuta River."
+// - Ariwara no Narihira
+assertEq(tByteSize("千"), s(24, 32)); // T, T
+assertEq(tByteSize("千早"), s(24, 32)); // T, T
+assertEq(tByteSize("千早ぶ"), s(24, 32)); // T, T
+assertEq(tByteSize("千早ぶる"), s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神"), s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神代"), s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神代も"), s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神代もき"), s(40, 40)); // F, F
+assertEq(tByteSize("千早ぶる神代もきかず龍"), s(40, 40)); // F, F
+assertEq(tByteSize("千早ぶる神代もきかず龍田"), s(56, 64)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 か"), s(56, 64)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 から"), s(72, 80)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水く"), s(72, 80)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くく"), s(88, 96)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くくるとは"), s(88, 96)); // X, X
+
+// A Latin-1 rope. This changes size when flattened.
+// "In a village of La Mancha, the name of which I have no desire to call to mind"
+// - Miguel de Cervantes, Don Quixote
+var fragment8 = "En un lugar de la Mancha, de cuyo nombre no quiero acordarme"; // 60 characters
+var rope8 = fragment8;
+for (var i = 0; i < 10; i++) // 1024 repetitions
+ rope8 = rope8 + rope8;
+assertEq(tByteSize(rope8), s(16, 24));
+var matches8 = rope8.match(/(de cuyo nombre no quiero acordarme)/);
+assertEq(tByteSize(rope8), s(16 + 65536, 24 + 65536));
+
+// Test extensible strings.
+//
+// Appending another copy of the fragment should yield another rope.
+//
+// Flatting that should turn the original rope into a dependent string, and
+// yield a new linear string, of the some size as the original.
+rope8a = rope8 + fragment8;
+assertEq(tByteSize(rope8a), s(16, 24));
+rope8a.match(/x/, function() { assertEq(true, false); });
+assertEq(tByteSize(rope8a), s(16 + 65536, 24 + 65536));
+assertEq(tByteSize(rope8), s(16, 24));
+
+
+// A char16_t rope. This changes size when flattened.
+// "From the Heliconian Muses let us begin to sing"
+// --- Hesiod, Theogony
+var fragment16 = "μουσάων Ἑλικωνιάδων ἀρχώμεθ᾽ ἀείδειν";
+var rope16 = fragment16;
+for (var i = 0; i < 10; i++) // 1024 repetitions
+ rope16 = rope16 + rope16;
+assertEq(tByteSize(rope16), s(16, 24));
+let matches16 = rope16.match(/(Ἑλικωνιάδων ἀρχώμεθ᾽)/);
+assertEq(tByteSize(rope16), s(16 + 131072, 24 + 131072));
+
+// Latin-1 and char16_t dependent strings.
+assertEq(tByteSize(rope8.substr(1000, 2000)), s(16, 24));
+assertEq(tByteSize(rope16.substr(1000, 2000)), s(16, 24));
+assertEq(tByteSize(matches8[0]), s(16, 24));
+assertEq(tByteSize(matches8[1]), s(16, 24));
+assertEq(tByteSize(matches16[0]), s(16, 24));
+assertEq(tByteSize(matches16[1]), s(16, 24));
+
+// Test extensible strings.
+//
+// Appending another copy of the fragment should yield another rope.
+//
+// Flatting that should turn the original rope into a dependent string, and
+// yield a new linear string, of the some size as the original.
+rope16a = rope16 + fragment16;
+assertEq(tByteSize(rope16a), s(16, 24));
+rope16a.match(/x/, function() { assertEq(true, false); });
+assertEq(tByteSize(rope16a), s(16 + 131072, 24 + 131072));
+assertEq(tByteSize(rope16), s(16, 24));
diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js
new file mode 100644
index 000000000..4a98bd424
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js
@@ -0,0 +1,25 @@
+// Check JS::ubi::Node::size results for symbols.
+
+// We actually hard-code specific sizes into this test, even though they're
+// implementation details, because in practice there are only two architecture
+// variants to consider (32-bit and 64-bit), and if these sizes change, that's
+// something SpiderMonkey hackers really want to know; they're supposed to be
+// stable.
+
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+var config = getBuildConfiguration();
+if (!config['moz-memory'])
+ quit(0);
+
+const SIZE_OF_SYMBOL = config['pointer-byte-size'] == 4 ? 16 : 24;
+
+// Without a description.
+assertEq(byteSize(Symbol()), SIZE_OF_SYMBOL);
+
+// With a description.
+assertEq(byteSize(Symbol("This is a relatively long description to be passed to "
+ + "Symbol() but it doesn't matter because it just gets "
+ + "interned as a JSAtom* anyways.")),
+ SIZE_OF_SYMBOL);
diff --git a/js/src/jit-test/tests/heap-analysis/findPath.js b/js/src/jit-test/tests/heap-analysis/findPath.js
new file mode 100644
index 000000000..7cff55dad
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/findPath.js
@@ -0,0 +1,48 @@
+load(libdir + "match.js")
+
+// At the moment, findPath just returns the names as provided by ubi::Node,
+// which just uses js::TraceChildren for now. However, we have various plans
+// to improve the quality of ubi::Node's metadata, to improve the precision
+// and clarity of the results here.
+
+var o = { w: { x: { y: { z: {} } } } };
+Match.Pattern([{node: {}, edge: "w"},
+ {node: {}, edge: "x"},
+ {node: {}, edge: "y"},
+ {node: {}, edge: "z"}])
+ .assert(findPath(o, o.w.x.y.z));
+print(uneval(findPath(o, o.w.x.y.z)));
+
+var a = [ , o ];
+Match.Pattern([{node: {}, edge: "objectElements[1]"}])
+ .assert(findPath(a, o));
+print(uneval(findPath(a, o)));
+
+function C() {}
+C.prototype.obj = {};
+var c = new C;
+Match.Pattern([{node: {}, edge: "group"},
+ {node: Match.Pattern.ANY, edge: "group_proto"},
+ {node: { constructor: Match.Pattern.ANY }, edge: "obj"}])
+ .assert(findPath(c, c.obj));
+print(uneval(findPath(c, c.obj)));
+
+function f(x) { return function g(y) { return x+y; }; }
+var o = {}
+var gc = f(o);
+Match.Pattern([{node: gc, edge: "fun_environment"},
+ {node: Match.Pattern.ANY, edge: "x"}])
+ .assert(findPath(gc, o));
+print(uneval(findPath(gc, o)));
+
+Match.Pattern([{node: {}, edge: "group"},
+ {node: Match.Pattern.ANY, edge: "group_global"},
+ {node: {}, edge: "o"}])
+ .assert(findPath(o, o));
+print(findPath(o, o).map((e) => e.edge).toString());
+
+// Check that we can generate ubi::Nodes for Symbols.
+var so = { sym: Symbol() };
+Match.Pattern([{node: {}, edge: "sym" }])
+ .assert(findPath(so, so.sym));
+print(findPath(so, so.sym).map((e) => e.edge).toString());
diff --git a/js/src/jit-test/tests/heap-analysis/pointerByteSize.js b/js/src/jit-test/tests/heap-analysis/pointerByteSize.js
new file mode 100644
index 000000000..617972deb
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/pointerByteSize.js
@@ -0,0 +1,3 @@
+// Try out the pointerByteSize shell function.
+var size = getBuildConfiguration()["pointer-byte-size"];
+assertEq(size == 4 || size == 8, true);
diff --git a/js/src/jit-test/tests/heap-analysis/shortestPaths.js b/js/src/jit-test/tests/heap-analysis/shortestPaths.js
new file mode 100644
index 000000000..d0c050c29
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/shortestPaths.js
@@ -0,0 +1,44 @@
+// The shortestPaths function exists solely to let the fuzzers go to town and
+// exercise the code paths it calls into, hence there is nothing to assert here.
+//
+// The actual behavior of JS::ubi::ShortestPaths is tested in
+// js/src/jsapi-tests/testUbiNode.cpp, where we can actually control the
+// structure of the heap graph to test specific shapes.
+
+function f(x) {
+ return x + x;
+}
+
+var g = f.bind(null, 5);
+
+var o = {
+ p: g
+};
+
+function dumpPaths(results) {
+ results = results.map(paths => {
+ return paths.map(path => {
+ return path.map(part => {
+ return {
+ predecessor: Object.prototype.toString.call(part.predecessor),
+ edge: part.edge
+ };
+ });
+ });
+ });
+ print(JSON.stringify(results, null, 2));
+}
+
+print("shortestPaths(this, [Object, f, o.p], 5)");
+var paths = shortestPaths(this, [Object, f, o.p], 5);
+dumpPaths(paths);
+
+print();
+print("shortestPaths(o, [f], 1)")
+paths = shortestPaths(o, [f], 1);
+dumpPaths(paths);
+
+print();
+print("shortestPaths(this, [f], 5)")
+paths = shortestPaths(this, [f], 5);
+dumpPaths(paths);