summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/cacheir/string-charAt-oob.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/cacheir/string-charAt-oob.js')
-rw-r--r--js/src/jit-test/tests/cacheir/string-charAt-oob.js151
1 files changed, 151 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/cacheir/string-charAt-oob.js b/js/src/jit-test/tests/cacheir/string-charAt-oob.js
new file mode 100644
index 0000000000..4de28d691a
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/string-charAt-oob.js
@@ -0,0 +1,151 @@
+// Test String.prototype.charAt with out-of-bounds indices.
+
+function* characters(...ranges) {
+ for (let [start, end] of ranges) {
+ for (let i = start; i <= end; ++i) {
+ yield i;
+ }
+ }
+}
+
+const empty = [];
+
+const ascii = [...characters(
+ [0x41, 0x5A], // A..Z
+ [0x61, 0x7A], // a..z
+)];
+
+const latin1 = [...characters(
+ [0xC0, 0xFF], // À..ÿ
+)];
+
+const twoByte = [...characters(
+ [0x100, 0x17E], // Ā..ž
+)];
+
+function atomize(s) {
+ return Object.keys({[s]: 0})[0];
+}
+
+function codePoints() {
+ return [empty, ascii, latin1, twoByte];
+}
+
+function toRope(s) {
+ // Ropes have at least two characters.
+ if (s.length < 2) {
+ return s;
+ }
+ if (s.length === 2) {
+ return newRope(s[0], s[1]);
+ }
+ return newRope(s[0], s.substring(1));
+}
+
+function makeStrings() {
+ let strings = codePoints()
+ .map(codePoints => String.fromCodePoint(...codePoints))
+ .flatMap(x => [
+ x,
+ toRope(x),
+ newString(x, {twoByte: true}),
+ atomize(x),
+ ]);
+ return strings;
+}
+
+function testNegativeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(-1);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexConstant();
+}
+
+function testNegativeIndexVariable() {
+ let indices = [-1, -2];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(indices[i & 1]);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeIndexVariable();
+}
+
+function testNegativeOrValidIndex() {
+ let indices = [-1, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.charAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : ""));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testNegativeOrValidIndex();
+}
+
+function testTooLargeIndexConstant() {
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(1000);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexConstant();
+}
+
+function testTooLargeIndexVariable() {
+ let indices = [1000, 2000];
+ let strings = makeStrings();
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let ch = str.charAt(indices[i & 1]);
+ assertEq(ch, "");
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeIndexVariable();
+}
+
+function testTooLargeOrValidIndex() {
+ let indices = [1000, 0];
+ let strings = makeStrings();
+
+ // Number of string kinds created in makeStrings.
+ const N = 4;
+
+ let cpoints = codePoints();
+ assertEq(strings.length, cpoints.length * N);
+
+ for (let i = 0; i < 200; ++i) {
+ let str = strings[i % strings.length];
+ let index = indices[i & 1];
+ let ch = str.charAt(index);
+
+ let cp = cpoints[Math.trunc((i % strings.length) / N)];
+ assertEq(ch, (0 <= index && index < cp.length ? String.fromCodePoint(cp[index]) : ""));
+ }
+}
+for (let i = 0; i < 2; ++i) {
+ testTooLargeOrValidIndex();
+}