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