From 81f66a5a3a5f1b4f342d49fe2807c7bdf44f8cb3 Mon Sep 17 00:00:00 2001 From: Jakob Gruber Date: Thu, 29 Sep 2016 19:41:37 +0200 Subject: [PATCH] Tests for new lastIndex semantics (#760) * Tests for new lastIndex semantics Add and update tests for the lastIndex semantic change introduced in https://github.com/tc39/ecma262/pull/627. * Address comments --- .../Symbol.match/builtin-coerce-lastindex.js | 2 +- ...=> builtin-failure-g-set-lastindex-err.js} | 13 ++-- ....js => builtin-failure-g-set-lastindex.js} | 13 ++-- ...r.js => builtin-y-coerce-lastindex-err.js} | 6 +- .../Symbol.search/lastindex-no-restore.js | 65 +++++++++++++++++++ .../set-lastindex-restore-err.js | 7 +- .../Symbol.search/set-lastindex-restore.js | 10 ++- .../exec/failure-g-lastindex-reset.js | 52 +++++++++++++++ .../exec/failure-lastindex-no-access.js | 33 ++++++++++ .../exec/success-g-lastindex-no-access.js | 33 ++++++++++ .../exec/success-lastindex-no-access.js | 33 ++++++++++ 11 files changed, 243 insertions(+), 24 deletions(-) rename test/built-ins/RegExp/prototype/Symbol.match/{builtin-failure-set-lastindex-err.js => builtin-failure-g-set-lastindex-err.js} (75%) rename test/built-ins/RegExp/prototype/Symbol.match/{builtin-failure-set-lastindex.js => builtin-failure-g-set-lastindex.js} (73%) rename test/built-ins/RegExp/prototype/Symbol.match/{builtin-coerce-lastindex-err.js => builtin-y-coerce-lastindex-err.js} (82%) create mode 100644 test/built-ins/RegExp/prototype/Symbol.search/lastindex-no-restore.js create mode 100644 test/built-ins/RegExp/prototype/exec/failure-g-lastindex-reset.js create mode 100644 test/built-ins/RegExp/prototype/exec/failure-lastindex-no-access.js create mode 100644 test/built-ins/RegExp/prototype/exec/success-g-lastindex-no-access.js create mode 100644 test/built-ins/RegExp/prototype/exec/success-lastindex-no-access.js diff --git a/test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex.js b/test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex.js index a16b3261ed..370d55a24f 100644 --- a/test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex.js +++ b/test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex.js @@ -17,7 +17,7 @@ info: > 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) [...] - 4. Let lastIndex be ToLength(Get(R,"lastIndex")). + 8. Else, let lastIndex be ? ToLength(? Get(R, "lastIndex")). features: [Symbol.match] ---*/ diff --git a/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-set-lastindex-err.js b/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex-err.js similarity index 75% rename from test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-set-lastindex-err.js rename to test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex-err.js index 4c3261feea..270f35fab5 100644 --- a/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-set-lastindex-err.js +++ b/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex-err.js @@ -21,18 +21,17 @@ info: > [...] 3. Let length be the number of code units in S. - 4. Let lastIndex be ToLength(Get(R,"lastIndex")). [...] - 14. Let matchSucceeded be false. - 15. Repeat, while matchSucceeded is false + 11. Let matchSucceeded be false. + 12. Repeat, while matchSucceeded is false a. If lastIndex > length, then - i. Let setStatus be Set(R, "lastIndex", 0, true). - ii. ReturnIfAbrupt(setStatus). - iii. Return null. + i. If global is true or sticky is true, then + 1. Perform ? Set(R, "lastIndex", 0, true). + ii. Return null. features: [Symbol.match] ---*/ -var r = /a/; +var r = /a/g; Object.defineProperty(r, 'lastIndex', { writable: false }); assert.throws(TypeError, function() { diff --git a/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-set-lastindex.js b/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex.js similarity index 73% rename from test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-set-lastindex.js rename to test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex.js index 6caa195cb4..e74fb589d7 100644 --- a/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-set-lastindex.js +++ b/test/built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex.js @@ -20,18 +20,17 @@ info: > [...] 3. Let length be the number of code units in S. - 4. Let lastIndex be ToLength(Get(R,"lastIndex")). [...] - 14. Let matchSucceeded be false. - 15. Repeat, while matchSucceeded is false + 11. Let matchSucceeded be false. + 12. Repeat, while matchSucceeded is false a. If lastIndex > length, then - i. Let setStatus be Set(R, "lastIndex", 0, true). - ii. ReturnIfAbrupt(setStatus). - iii. Return null. + i. If global is true or sticky is true, then + 1. Perform ? Set(R, "lastIndex", 0, true). + ii. Return null. features: [Symbol.match] ---*/ -var r = /a/; +var r = /a/g; r.lastIndex = 3; r[Symbol.match]('b'); diff --git a/test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex-err.js b/test/built-ins/RegExp/prototype/Symbol.match/builtin-y-coerce-lastindex-err.js similarity index 82% rename from test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex-err.js rename to test/built-ins/RegExp/prototype/Symbol.match/builtin-y-coerce-lastindex-err.js index 9b3fd43846..96efed7749 100644 --- a/test/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex-err.js +++ b/test/built-ins/RegExp/prototype/Symbol.match/builtin-y-coerce-lastindex-err.js @@ -17,12 +17,12 @@ info: > 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) [...] - 4. Let lastIndex be ToLength(Get(R,"lastIndex")). - 5. ReturnIfAbrupt(lastIndex). + 7. If global is false and sticky is false, let lastIndex be 0. + 8. Else, let lastIndex be ? ToLength(? Get(R, "lastIndex")). features: [Symbol.match] ---*/ -var r = /./; +var r = /./y; r.lastIndex = { valueOf: function() { throw new Test262Error(); diff --git a/test/built-ins/RegExp/prototype/Symbol.search/lastindex-no-restore.js b/test/built-ins/RegExp/prototype/Symbol.search/lastindex-no-restore.js new file mode 100644 index 0000000000..d8b1eae621 --- /dev/null +++ b/test/built-ins/RegExp/prototype/Symbol.search/lastindex-no-restore.js @@ -0,0 +1,65 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +es6id: 21.2.5.9 +description: Behavior when some lastIndex writes should be skipped. +info: > + [...] + 4. Let previousLastIndex be ? Get(rx, "lastIndex"). + 5. If SameValue(previousLastIndex, 0) is false, then + a. Perform ? Set(rx, "lastIndex", 0, true). + [...] + 7. Let currentLastIndex be ? Get(rx, "lastIndex"). + 8. If SameValue(currentLastIndex, previousLastIndex) is false, then + a. Perform ? Set(rx, "lastIndex", previousLastIndex, true). + [...] +features: [Symbol.search] +---*/ + +var lastIndexValue; +var lastIndexValueAfterExec; +var lastIndexReads; +var lastIndexWrites; +var execCallCount; +var result; + +var fakeRe = { + get lastIndex() { + lastIndexReads++; + return lastIndexValue; + }, + set lastIndex(_) { + lastIndexWrites++; + lastIndexValue = _; + }, + exec: function() { + execCallCount++; + lastIndexValue = lastIndexValueAfterExec; + return null; + } +}; + +function reset(value, valueAfterExec) { + lastIndexValue = value; + lastIndexValueAfterExec = valueAfterExec; + lastIndexReads = 0; + lastIndexWrites = 0; + execCallCount = 0; +} + +reset(0, 0); +result = RegExp.prototype[Symbol.search].call(fakeRe); +assert.sameValue(result, -1); +assert.sameValue(lastIndexValue, 0); +assert.sameValue(lastIndexReads, 2); +assert.sameValue(lastIndexWrites, 0); +assert.sameValue(execCallCount, 1); + +reset(420, 420); +result = RegExp.prototype[Symbol.search].call(fakeRe); +assert.sameValue(result, -1); +assert.sameValue(lastIndexValue, 420); +assert.sameValue(lastIndexReads, 2); +assert.sameValue(lastIndexWrites, 1); +assert.sameValue(execCallCount, 1); diff --git a/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore-err.js b/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore-err.js index 9a3ede8316..0201d4b0c3 100644 --- a/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore-err.js +++ b/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore-err.js @@ -8,18 +8,19 @@ description: > match execution info: > [...] - 11. Let status be Set(rx, "lastIndex", previousLastIndex, true). - 12. ReturnIfAbrupt(status). + 8. If SameValue(currentLastIndex, previousLastIndex) is false, then + a. Perform ? Set(rx, "lastIndex", previousLastIndex, true). features: [Symbol.search] ---*/ var callCount; var poisonedLastIndex = { - get lastIndex() {}, + get lastIndex() { return this.lastIndex_; }, set lastIndex(_) { if (callCount === 1) { throw new Test262Error(); } + this.lastIndex_ = _; }, exec: function() { callCount += 1; diff --git a/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore.js b/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore.js index 9b18b522df..e1c9286910 100644 --- a/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore.js +++ b/test/built-ins/RegExp/prototype/Symbol.search/set-lastindex-restore.js @@ -6,20 +6,23 @@ es6id: 21.2.5.9 description: The `lastIndex` value is restored following match execution info: > [...] - 11. Let status be Set(rx, "lastIndex", previousLastIndex, true). + 8. If SameValue(currentLastIndex, previousLastIndex) is false, then + a. Perform ? Set(rx, "lastIndex", previousLastIndex, true). [...] features: [Symbol.search] ---*/ -var latestValue; +var latestValue = 86; +var callCount = 0; var fakeRe = { get lastIndex() { - return 86; + return latestValue; }, set lastIndex(_) { latestValue = _; }, exec: function() { + callCount++; latestValue = null; return null; } @@ -27,4 +30,5 @@ var fakeRe = { RegExp.prototype[Symbol.search].call(fakeRe); +assert.sameValue(callCount, 1); assert.sameValue(latestValue, 86); diff --git a/test/built-ins/RegExp/prototype/exec/failure-g-lastindex-reset.js b/test/built-ins/RegExp/prototype/exec/failure-g-lastindex-reset.js new file mode 100644 index 0000000000..01d1353604 --- /dev/null +++ b/test/built-ins/RegExp/prototype/exec/failure-g-lastindex-reset.js @@ -0,0 +1,52 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: lastIndex is read and reset to 0 when global is set and the match + fails. +es6id: 21.2.5.2.2 +info: > + 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) + + [...] + 4. Let flags be R.[[OriginalFlags]]. + 5. If flags contains "g", let global be true, else let global be false. + [...] + 7. If global is false and sticky is false, let lastIndex be 0. + 8. Else, let lastIndex be ? ToLength(? Get(R, "lastIndex")). + [...] + 12. Repeat, while matchSucceeded is false + [...] + c. If r is failure, then + i. If sticky is true, then + 1. Perform ? Set(R, "lastIndex", 0, true). + 2. Return null. + ii. Let lastIndex be AdvanceStringIndex(S, lastIndex, fullUnicode). +---*/ + +var lastIndexReads; +var result; + +var r = /a/g; + +function reset(value) { + r.lastIndex = { + valueOf: function() { + lastIndexReads++; + return value; + } + }; + lastIndexReads = 0; +} + +reset(42); // lastIndex beyond end of string. +result = r.exec('abc'); +assert.sameValue(result, null); +assert.sameValue(r.lastIndex, 0); +assert.sameValue(lastIndexReads, 1); + +reset(-1); // No match. +result = r.exec('nbc'); +assert.sameValue(result, null); +assert.sameValue(r.lastIndex, 0); +assert.sameValue(lastIndexReads, 1); diff --git a/test/built-ins/RegExp/prototype/exec/failure-lastindex-no-access.js b/test/built-ins/RegExp/prototype/exec/failure-lastindex-no-access.js new file mode 100644 index 0000000000..6bf728a658 --- /dev/null +++ b/test/built-ins/RegExp/prototype/exec/failure-lastindex-no-access.js @@ -0,0 +1,33 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: lastIndex is not accessed when global and sticky are unset. +es6id: 21.2.5.2.2 +info: > + 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) + + [...] + 7. If global is false and sticky is false, let lastIndex be 0. + [...] + 12. Repeat, while matchSucceeded is false + [...] + c. If r is failure, then + i. If sticky is true, then + 1. Perform ? Set(R, "lastIndex", 0, true). + 2. Return null. +---*/ + +var thrower = { + valueOf: function() { + throw new Test262Error(); + } +}; + +var r = /a/; +r.lastIndex = thrower; + +var result = r.exec('nbc'); +assert.sameValue(result, null); +assert.sameValue(r.lastIndex, thrower); + diff --git a/test/built-ins/RegExp/prototype/exec/success-g-lastindex-no-access.js b/test/built-ins/RegExp/prototype/exec/success-g-lastindex-no-access.js new file mode 100644 index 0000000000..a6166e804c --- /dev/null +++ b/test/built-ins/RegExp/prototype/exec/success-g-lastindex-no-access.js @@ -0,0 +1,33 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: lastIndex is accessed when global is set. +es6id: 21.2.5.2.2 +info: > + 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) + + [...] + 4. Let flags be R.[[OriginalFlags]]. + 5. If flags contains "g", let global be true, else let global be false. + [...] + 15. If global is true or sticky is true, then + a. Perform ? Set(R, "lastIndex", e, true). +---*/ + +var lastIndexReads = 0; + +var r = /./g; +r.lastIndex = { + valueOf: function() { + lastIndexReads++; + return 0; + } +}; + +var result = r.exec('abc'); +assert.sameValue(result.length, 1); +assert.sameValue(result[0], 'a'); +assert.sameValue(r.lastIndex, 1); +assert.sameValue(lastIndexReads, 1); + diff --git a/test/built-ins/RegExp/prototype/exec/success-lastindex-no-access.js b/test/built-ins/RegExp/prototype/exec/success-lastindex-no-access.js new file mode 100644 index 0000000000..e95354c7b2 --- /dev/null +++ b/test/built-ins/RegExp/prototype/exec/success-lastindex-no-access.js @@ -0,0 +1,33 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: lastIndex is not accessed when global and sticky are unset. +es6id: 21.2.5.2.2 +info: > + 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) + + [...] + 4. Let flags be R.[[OriginalFlags]]. + 5. If flags contains "g", let global be true, else let global be false. + [...] + 15. If global is true or sticky is true, then + a. Perform ? Set(R, "lastIndex", e, true). +---*/ + +var thrower = { + valueOf: function() { + throw new Test262Error(); + } +}; + +var r = /./; +r.lastIndex = thrower; + +var result = r.exec('abc'); + +assert.notSameValue(result, null); +assert.sameValue(result.length, 1); +assert.sameValue(result[0], 'a'); +assert.sameValue(r.lastIndex, thrower); +