Add tests for regexp-match-indices (#2309)

* Add tests for regexp-match-indices

* Add tests for unicode matches based on presence of /u flag

* Added deepEqual helper, PR feedback
This commit is contained in:
Ron Buckton 2019-09-18 09:39:12 -07:00 committed by Leo Balter
parent c41a8ac1a0
commit 1056d8fde9
25 changed files with 1156 additions and 5 deletions

View File

@ -141,6 +141,10 @@ optional-chaining
# https://github.com/tc39/proposal-top-level-await
top-level-await
# RegExp Match Array Indices
# https://github.com/tc39/proposal-regexp-match-indices
regexp-match-indices
## Standard language features
#
# Language features that have been included in a published version of the

View File

@ -5,6 +5,9 @@ description: |
Collection of assertion functions used throughout test262
---*/
/// <reference lib="esnext" />
/// <reference path="./types.d.ts" />
function assert(mustBeTrue, message) {
if (mustBeTrue === true) {
return;
@ -92,6 +95,58 @@ assert.throws = function (expectedErrorConstructor, func, message) {
$ERROR(message);
};
assert._formatValue = function(value, seen) {
switch (typeof value) {
case 'string':
return typeof JSON !== "undefined" ? JSON.stringify(value) : '"' + value + '"';
case 'number':
case 'boolean':
case 'symbol':
case 'bigint':
return value.toString();
case 'undefined':
return 'undefined';
case 'function':
return '[Function' + (value.name ? ': ' + value.name : '') + ']';
case 'object':
if (value === null) return 'null';
if (value instanceof Date) return 'Date "' + value.toISOString() + '"';
if (value instanceof RegExp) return value.toString();
if (!seen) {
seen = {
counter: 0,
map: new Map()
};
}
var usage = seen.map.get(value);
if (usage) {
usage.used = true;
return '[Ref: #' + usage.id + ']';
}
usage = { id: ++seen.counter, used: false };
seen.map.set(value, usage);
if (typeof Set !== "undefined" && value instanceof Set) {
return 'Set {' + Array.from(value).map(function (value) { return assert._formatValue(value, seen); }).join(', ') + '}' + (usage.used ? ' as #' + usage.id : '');
}
if (typeof Map !== "undefined" && value instanceof Map) {
return 'Map {' + Array.from(value).map(function (pair) { return assert._formatValue(pair[0], seen) + ' => ' + assert._formatValue(pair[1], seen) + '}'; }).join(', ') + '}' + (usage.used ? ' as #' + usage.id : '');
}
if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
return '[' + value.map(function (value) { return assert._formatValue(value, seen); }).join(', ') + ']' + (usage.used ? ' as #' + usage.id : '');
}
var tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
tag = '[Object: null prototype]';
}
return (tag ? tag + ' ' : '') + '{ ' + Object.keys(value).map(function (key) { return key.toString() + ': ' + assert._formatValue(value[key], seen); }).join(', ') + ' }' + (usage.used ? ' as #' + usage.id : '');
default:
return typeof value;
}
};
assert._toString = function (value) {
try {
return String(value);

View File

@ -5,6 +5,9 @@ description: |
Compare the contents of two arrays
---*/
// @ts-check
/// <reference path="./assert.js" />
function isSameValue(a, b) {
if (a === 0 && b === 0) return 1 / a === 1 / b;
if (a !== a && b !== b) return true;
@ -12,6 +15,11 @@ function isSameValue(a, b) {
return a === b;
}
/**
* @template T
* @param {T[]} a
* @param {T[]} b
*/
function compareArray(a, b) {
if (b.length !== a.length) {
return false;
@ -25,11 +33,13 @@ function compareArray(a, b) {
return true;
}
/**
* @template T
* @param {T[]} actual
* @param {T[]} expected
* @param {string} [message]
*/
assert.compareArray = function(actual, expected, message) {
function formatArray(array) {
return '[' + array.map(String).join(', ') + ']';
}
assert(compareArray(actual, expected),
'Expected ' + formatArray(actual) + ' and ' + formatArray(expected) + ' to have the same contents. ' + message);
'Expected ' + assert._formatValue(actual) + ' and ' + assert._formatValue(expected) + ' to have the same contents. ' + message);
};

415
harness/deepEqual.js Normal file
View File

@ -0,0 +1,415 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
Compare two values structurally
---*/
// @ts-check
/// <reference path="./assert.js" />
var deepEqual = (function () {
/**
* @typedef {0} UNKNOWN
* @typedef {1} EQUAL
* @typedef {-1} NOT_EQUAL
* @typedef {Map<unknown, Map<unknown, EQUAL | NOT_EQUAL>>} ComparisonCache
*/
/** @type {EQUAL} */
var EQUAL = 1;
/** @type {NOT_EQUAL} */
var NOT_EQUAL = -1;
/** @type {UNKNOWN} */
var UNKNOWN = 0;
/**
* @template T
* @param {T} a
* @param {T} b
* @returns {boolean}
*/
function deepEqual(a, b) {
return compareEquality(a, b) === EQUAL;
}
/**
* @param {unknown} a
* @param {unknown} b
* @param {ComparisonCache} [cache]
* @returns {EQUAL | NOT_EQUAL}
*/
function compareEquality(a, b, cache) {
return compareIf(a, b, isOptional, compareOptionality)
|| compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
|| compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
|| NOT_EQUAL;
}
/**
* @template T
* @param {unknown} a
* @param {unknown} b
* @param {(value: unknown) => value is T} test
* @param {(a: T, b: T, cache?: ComparisonCache) => EQUAL | NOT_EQUAL} compare
* @param {ComparisonCache} [cache]
* @returns {EQUAL | NOT_EQUAL | UNKNOWN}
*/
function compareIf(a, b, test, compare, cache) {
return !test(a)
? !test(b) ? UNKNOWN : NOT_EQUAL
: !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
}
/**
* @returns {EQUAL | UNKNOWN}
*/
function tryCompareStrictEquality(a, b) {
return a === b ? EQUAL : UNKNOWN;
}
/**
* @returns {NOT_EQUAL | UNKNOWN}
*/
function tryCompareTypeOfEquality(a, b) {
return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
}
/**
* @returns {NOT_EQUAL | UNKNOWN}
*/
function tryCompareToStringTagEquality(a, b) {
var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
}
/**
* @returns {value is null | undefined}
*/
function isOptional(value) {
return value === undefined
|| value === null;
}
/**
* @returns {EQUAL | NOT_EQUAL}
*/
function compareOptionality(a, b) {
return tryCompareStrictEquality(a, b)
|| NOT_EQUAL;
}
/**
* @returns {value is number | bigint | string | symbol | boolean | undefined}
*/
function isPrimitiveEquatable(value) {
switch (typeof value) {
case 'string':
case 'number':
case 'bigint':
case 'boolean':
case 'symbol':
return true;
default:
return isBoxed(value);
}
}
/**
* @returns {EQUAL | NOT_EQUAL}
*/
function comparePrimitiveEquality(a, b) {
if (isBoxed(a)) a = a.valueOf();
if (isBoxed(b)) b = b.valueOf();
return tryCompareStrictEquality(a, b)
|| tryCompareTypeOfEquality(a, b)
|| compareIf(a, b, isNaNEquatable, compareNaNEquality)
|| NOT_EQUAL;
}
/**
* @returns {value is number}
*/
function isNaNEquatable(value) {
return typeof value === 'number';
}
/**
* @param {number} a
* @param {number} b
* @returns {EQUAL | NOT_EQUAL}
*/
function compareNaNEquality(a, b) {
return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
}
/**
* @returns {value is object}
*/
function isObjectEquatable(value) {
return typeof value === 'object';
}
/**
* @param {ComparisonCache} cache
* @returns {EQUAL | NOT_EQUAL}
*/
function compareObjectEquality(a, b, cache) {
if (!cache) cache = new Map();
return getCache(cache, a, b)
|| setCache(cache, a, b, EQUAL) // consider equal for now
|| cacheComparison(a, b, tryCompareStrictEquality, cache)
|| cacheComparison(a, b, tryCompareToStringTagEquality, cache)
|| compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
|| compareIf(a, b, isToStringEquatable, compareToStringEquality)
|| compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
|| compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
|| compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
|| cacheComparison(a, b, fail, cache);
}
function isBoxed(value) {
return value instanceof String
|| value instanceof Number
|| value instanceof Boolean
|| typeof Symbol === 'function' && value instanceof Symbol
|| typeof BigInt === 'function' && value instanceof BigInt;
}
/**
* @returns {value is { valueOf(): any }}
*/
function isValueOfEquatable(value) {
return value instanceof Date;
}
/**
* @param {{ valueOf(): any }} a
* @param {{ valueOf(): any }} b
* @returns {EQUAL | NOT_EQUAL}
*/
function compareValueOfEquality(a, b) {
return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
|| NOT_EQUAL;
}
/**
* @returns {value is { toString(): string }}
*/
function isToStringEquatable(value) {
return value instanceof RegExp;
}
/**
* @param {{ toString(): string }} a
* @param {{ toString(): string }} b
* @returns {EQUAL | NOT_EQUAL}
*/
function compareToStringEquality(a, b) {
return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
|| NOT_EQUAL;
}
/**
* @returns {value is ArrayLike<unknown>}
*/
function isArrayLikeEquatable(value) {
return (Array.isArray ? Array.isArray(value) : value instanceof Array)
|| (typeof Uint8Array === 'function' && value instanceof Uint8Array)
|| (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
|| (typeof Uint16Array === 'function' && value instanceof Uint16Array)
|| (typeof Uint32Array === 'function' && value instanceof Uint32Array)
|| (typeof Int8Array === 'function' && value instanceof Int8Array)
|| (typeof Int16Array === 'function' && value instanceof Int16Array)
|| (typeof Int32Array === 'function' && value instanceof Int32Array)
|| (typeof Float32Array === 'function' && value instanceof Float32Array)
|| (typeof Float64Array === 'function' && value instanceof Float64Array)
|| (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
|| (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
}
/**
* @template T
* @param {ArrayLike<T>} a
* @param {ArrayLike<T>} b
* @param {ComparisonCache} cache
* @returns {EQUAL | NOT_EQUAL}
*/
function compareArrayLikeEquality(a, b, cache) {
if (a.length !== b.length) return NOT_EQUAL;
for (var i = 0; i < a.length; i++) {
if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
return NOT_EQUAL;
}
}
return EQUAL;
}
/**
* @template T
* @param {T} value
* @returns {value is Exclude<T, Promise | WeakMap | WeakSet | Map | Set>}
*/
function isStructurallyEquatable(value) {
return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
|| typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
|| typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
|| typeof Map === 'function' && value instanceof Map // comparable via @@iterator
|| typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
}
/**
* @param {ComparisonCache} cache
* @returns {EQUAL | NOT_EQUAL}
*/
function compareStructuralEquality(a, b, cache) {
var aKeys = [];
for (var key in a) aKeys.push(key);
var bKeys = [];
for (var key in b) bKeys.push(key);
if (aKeys.length !== bKeys.length) {
return NOT_EQUAL;
}
aKeys.sort();
bKeys.sort();
for (var i = 0; i < aKeys.length; i++) {
var aKey = aKeys[i];
var bKey = bKeys[i];
if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
return NOT_EQUAL;
}
if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
return NOT_EQUAL;
}
}
return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
|| EQUAL;
}
/**
* @returns {value is Iterable<unknown>}
*/
function isIterableEquatable(value) {
return typeof Symbol === 'function'
&& typeof value[Symbol.iterator] === 'function';
}
/**
* @template T
* @param {Iterator<T>} a
* @param {Iterator<T>} b
* @param {ComparisonCache} cache
* @returns {EQUAL | NOT_EQUAL}
*/
function compareIteratorEquality(a, b, cache) {
if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
typeof Set === 'function' && a instanceof Set && b instanceof Set) {
if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
}
var ar, br;
while (true) {
ar = a.next();
br = b.next();
if (ar.done) {
if (br.done) return EQUAL;
if (b.return) b.return();
return NOT_EQUAL;
}
if (br.done) {
if (a.return) a.return();
return NOT_EQUAL;
}
if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
if (a.return) a.return();
if (b.return) b.return();
return NOT_EQUAL;
}
}
}
/**
* @template T
* @param {Iterable<T>} a
* @param {Iterable<T>} b
* @param {ComparisonCache} cache
* @returns {EQUAL | NOT_EQUAL}
*/
function compareIterableEquality(a, b, cache) {
return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
}
/**
* @template T
* @template {EQUAL | NOT_EQUAL | UNKNOWN} R
* @param {(a: T, b: T, circular?: ComparisonCache) => R} compare
* @param {ComparisonCache} [cache]
*/
function cacheComparison(a, b, compare, cache) {
var result = compare(a, b, cache);
if (cache && (result === EQUAL || result === NOT_EQUAL)) {
setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
}
return result;
}
function fail() {
return NOT_EQUAL;
}
/**
* @param {EQUAL | NOT_EQUAL} result
* @param {ComparisonCache} cache
*/
function setCache(cache, left, right, result) {
var otherCache;
otherCache = cache.get(left);
if (!otherCache) cache.set(left, otherCache = new Map());
otherCache.set(right, result);
otherCache = cache.get(right);
if (!otherCache) cache.set(right, otherCache = new Map());
otherCache.set(left, result);
}
/**
* @param {ComparisonCache} cache
*/
function getCache(cache, left, right) {
var otherCache;
/** @type {EQUAL | NOT_EQUAL | UNKNOWN | undefined} */
var result;
otherCache = cache.get(left);
result = otherCache && otherCache.get(right);
if (result) return result;
otherCache = cache.get(right);
result = otherCache && otherCache.get(left);
if (result) return result;
return UNKNOWN;
}
return deepEqual;
})();
/**
* @template T
* @param {T} actual
* @param {T} expected
* @param {string} [message]
*/
assert.deepEqual = function (actual, expected, message) {
assert(deepEqual(actual, expected),
'Expected ' + assert._formatValue(actual) + ' to be structurally equal to ' + assert._formatValue(expected) + '. ' + (message || ''));
};

View File

@ -6,6 +6,16 @@ description: |
property descriptors.
---*/
// @ts-check
/// <reference path="./assert.js" />
/**
* @param {object} obj
* @param {string|symbol} name
* @param {PropertyDescriptor|undefined} desc
* @param {object} [options]
* @param {boolean} [options.restore]
*/
function verifyProperty(obj, name, desc, options) {
assert(
arguments.length > 2,

14
harness/types.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
declare function $ERROR(text: string): void;
// Proposal: regexp-match-indices
interface RegExpExecArray {
indices: RegExpIndicesArray;
}
interface RegExpMatchArray {
indices: RegExpIndicesArray;
}
interface RegExpIndicesArray extends Array<[number, number]> {
groups?: { [group: string]: [number, number] };
}

View File

@ -0,0 +1,31 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: A matching element of indices is an Array with exactly two number properties.
includes: [compareArray.js]
esid: sec-getmatchindicesarray
features: [regexp-match-indices]
info: |
GetMatchIndicesArray ( S, match )
5. Return CreateArrayFromList(« _match_.[[StartIndex]], _match_.[[EndIndex]] »).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
let input = "abcd";
let match = /b(c)/.exec(input);
let indices = match.indices;
// `indices[0]` is an array
assert.sameValue(Object.getPrototypeOf(indices[0]), Array.prototype);
assert.sameValue(indices[0].length, 2);
assert.sameValue(typeof indices[0][0], "number");
assert.sameValue(typeof indices[0][1], "number");
// `indices[1]` is an array
assert.sameValue(Object.getPrototypeOf(indices[1]), Array.prototype);
assert.sameValue(indices[1].length, 2);
assert.sameValue(typeof indices[1][0], "number");
assert.sameValue(typeof indices[1][1], "number");

View File

@ -0,0 +1,41 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: The properties of the "indices" array correspond to the start/end indices of the same values in the match.
includes: [compareArray.js]
esid: sec-makeindicesarray
features: [regexp-match-indices]
info: |
MakeIndicesArray ( S, indices, groupNames )
4. Let _n_ be the number of elements in _indices_.
...
6. Set _A_ to ! ArrayCreate(_n_).
...
11. For each integer _i_ such that _i_ >= 0 and _i_ < _n_, do
a. Let _matchIndices_ be _indices_[_i_].
b. If _matchIndices_ is not *undefined*, then
i. Let _matchIndicesArray_ be ! GetMatchIndicesArray(_S_, _matchIndices_).
c. Else,
i. Let _matchIndicesArray_ be *undefined*.
d. Perform ! CreateDataProperty(_A_, ! ToString(_n_), _matchIndicesArray_).
...
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
let input = "abcd";
let match = /b(c)/.exec(input);
let indices = match.indices;
// `indices` has the same length as match
assert.sameValue(indices.length, match.length);
// The first element of `indices` contains the start/end indices of the match
assert.compareArray(indices[0], [1, 3]);
assert.sameValue(input.slice(indices[0][0], indices[0][1]), match[0]);
// The second element of `indices` contains the start/end indices of the first capture
assert.compareArray(indices[1], [2, 3]);
assert.sameValue(input.slice(indices[1][0], indices[1][1]), match[1]);

View File

@ -0,0 +1,80 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Basic matching cases with non-unicode matches.
includes: [compareArray.js, propertyHelper.js, deepEqual.js]
esid: sec-regexpbuiltinexec
features: [regexp-match-indices]
info: |
Runtime Semantics: RegExpBuiltinExec ( R, S )
...
4. Let _lastIndex_ be ? ToLength(? Get(_R_, `"lastIndex")).
...
25. Let _indices_ be a new empty List.
26. Let _match_ be the Match { [[StartIndex]]: _lastIndex_, [[EndIndex]]: _e_ }.
27. Add _match_ as the last element of _indices_.
...
33. For each integer _i_ such that _i_ > 0 and _i_ <= _n_, in ascending order, do
...
f. Else,
i. Let _captureStart_ be _captureI_'s _startIndex_.
ii. Let _captureEnd_ be _captureI_'s _endIndex_.
...
iv. Let _capture_ be the Match { [[StartIndex]]: _captureStart_, [[EndIndex]]: _captureEnd_ }.
v. Append _capture_ to _indices_.
...
34. Let _indicesArray_ be MakeIndicesArray( _S_, _indices_, _groupNames_).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
/// <reference path="../../../../harness/deepEqual.js" />
assert.deepEqual([[1, 2], [1, 2]], "bab".match(/(a)/).indices);
assert.deepEqual([[0, 3], [1, 2]], "bab".match(/.(a)./).indices);
assert.deepEqual([[0, 3], [1, 2], [2, 3]], "bab".match(/.(a)(.)/).indices);
assert.deepEqual([[0, 3], [1, 3]], "bab".match(/.(\w\w)/).indices);
assert.deepEqual([[0, 3], [0, 3]], "bab".match(/(\w\w\w)/).indices);
assert.deepEqual([[0, 3], [0, 2], [2, 3]], "bab".match(/(\w\w)(\w)/).indices);
assert.deepEqual([[0, 2], [0, 2], undefined], "bab".match(/(\w\w)(\W)?/).indices);
let groups = /(?<a>.)(?<b>.)(?<c>.)\k<c>\k<b>\k<a>/.exec("abccba").indices.groups;
assert.compareArray([0, 1], groups.a);
assert.compareArray([1, 2], groups.b);
assert.compareArray([2, 3], groups.c);
verifyProperty(groups, "a", {
enumerable: true,
writable: true,
configurable: true
});
verifyProperty(groups, "b", {
enumerable: true,
writable: true,
configurable: true
});
verifyProperty(groups, "c", {
enumerable: true,
writable: true,
configurable: true
});
// "𝐁" is U+1d401 MATHEMATICAL BOLD CAPITAL B
// - Also representable as the code point "\u{1d401}"
// - Also representable as the surrogate pair "\uD835\uDC01"
// Verify assumptions:
assert.sameValue("𝐁".length, 2, 'The length of "𝐁" is 2');
assert.sameValue("\u{1d401}".length, 2, 'The length of "\\u{1d401}" is 2');
assert.sameValue("\uD835\uDC01".length, 2, 'The length of "\\uD835\\uDC01" is 2');
assert.sameValue("𝐁".match(/./)[0].length, 1, 'The length of a single code unit match against "𝐁" is 1 (without /u flag)');
assert.sameValue("\u{1d401}".match(/./)[0].length, 1, 'The length of a single code unit match against "\\u{1d401}" is 1 (without /u flag)');
assert.sameValue("\uD835\uDC01".match(/./)[0].length, 1, 'The length of a single code unit match against "\\ud835\\udc01" is 1 (without /u flag)');
assert.compareArray([0, 1], "𝐁".match(/./).indices[0], 'Indices for non-unicode match against "𝐁" (without /u flag)');
assert.compareArray([0, 1], "\u{1d401}".match(/./).indices[0], 'Indices for non-unicode match against "\\u{1d401}" (without /u flag)');
assert.compareArray([0, 1], "\uD835\uDC01".match(/./).indices[0], 'Indices for non-unicode match against "\\ud835\\udc01" (without /u flag)');
assert.compareArray([0, 1], "𝐁".match(/(?<a>.)/).indices.groups.a, 'Indices for non-unicode match against "𝐁" in groups.a (without /u flag)');
assert.compareArray([0, 1], "\u{1d401}".match(/(?<a>.)/).indices.groups.a, 'Indices for non-unicode match against "\\u{1d401}" in groups.a (without /u flag)');
assert.compareArray([0, 1], "\uD835\uDC01".match(/(?<a>.)/).indices.groups.a, 'Indices for non-unicode match against "\\ud835\\udc01" in groups.a (without /u flag)');

View File

@ -0,0 +1,32 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: The properties of the "indices" array are created with CreateDataProperty.
includes: [propertyHelper.js]
esid: sec-makeindicesarray
features: [regexp-match-indices]
info: |
MakeIndicesArray ( S, indices, groupNames )
11. For each integer _i_ such that _i_ >= 0 and _i_ < _n_, do
d. Perform ! CreateDataProperty(_A_, ! ToString(_n_), _matchIndicesArray_).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
let input = "abcd";
let match = /b(c)/.exec(input);
let indices = match.indices;
verifyProperty(indices, '0', {
enumerable: true,
configurable: true,
writable: true
});
verifyProperty(indices, '1', {
enumerable: true,
configurable: true,
writable: true
});

View File

@ -0,0 +1,89 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Basic matching cases with non-unicode matches.
includes: [compareArray.js, propertyHelper.js, deepEqual.js]
esid: sec-regexpbuiltinexec
features: [regexp-match-indices]
info: |
Runtime Semantics: RegExpBuiltinExec ( R, S )
...
4. Let _lastIndex_ be ? ToLength(? Get(_R_, `"lastIndex")).
...
16. If _fullUnicode_ is *true*, set _e_ to ! GetStringIndex(_S_, _Input_, _e_).
...
25. Let _indices_ be a new empty List.
26. Let _match_ be the Match { [[StartIndex]]: _lastIndex_, [[EndIndex]]: _e_ }.
27. Add _match_ as the last element of _indices_.
...
33. For each integer _i_ such that _i_ > 0 and _i_ <= _n_, in ascending order, do
...
f. Else,
i. Let _captureStart_ be _captureI_'s _startIndex_.
ii. Let _captureEnd_ be _captureI_'s _endIndex_.
iii. If _fullUnicode_ is *true*, then
1. Set _captureStart_ to ! GetStringIndex(_S_, _Input_, _captureStart_).
1. Set _captureEnd_ to ! GetStringIndex(_S_, _Input_, _captureEnd_).
iv. Let _capture_ be the Match { [[StartIndex]]: _captureStart_, [[EndIndex]]: _captureEnd_ }.
v. Append _capture_ to _indices_.
...
34. Let _indicesArray_ be MakeIndicesArray( _S_, _indices_, _groupNames_).
GetStringIndex ( S, Input, e )
...
4. Let _eUTF_ be the smallest index into _S_ that corresponds to the character at element _e_ of _Input_. If _e_ is greater than or equal to the number of elements in _Input_, then _eUTF_ is the number of code units in _S_.
5. Return _eUTF_.
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
/// <reference path="../../../../harness/deepEqual.js" />
assert.deepEqual([[1, 2], [1, 2]], "bab".match(/(a)/u).indices);
assert.deepEqual([[0, 3], [1, 2]], "bab".match(/.(a)./u).indices);
assert.deepEqual([[0, 3], [1, 2], [2, 3]], "bab".match(/.(a)(.)/u).indices);
assert.deepEqual([[0, 3], [1, 3]], "bab".match(/.(\w\w)/u).indices);
assert.deepEqual([[0, 3], [0, 3]], "bab".match(/(\w\w\w)/u).indices);
assert.deepEqual([[0, 3], [0, 2], [2, 3]], "bab".match(/(\w\w)(\w)/u).indices);
assert.deepEqual([[0, 2], [0, 2], undefined], "bab".match(/(\w\w)(\W)?/u).indices);
let groups = /(?<a>.)(?<b>.)(?<c>.)\k<c>\k<b>\k<a>/u.exec("abccba").indices.groups;
assert.compareArray([0, 1], groups.a);
assert.compareArray([1, 2], groups.b);
assert.compareArray([2, 3], groups.c);
verifyProperty(groups, "a", {
enumerable: true,
writable: true,
configurable: true
});
verifyProperty(groups, "b", {
enumerable: true,
writable: true,
configurable: true
});
verifyProperty(groups, "c", {
enumerable: true,
writable: true,
configurable: true
});
// "𝐁" is U+1d401 MATHEMATICAL BOLD CAPITAL B
// - Also representable as the code point "\u{1d401}"
// - Also representable as the surrogate pair "\uD835\uDC01"
// Verify assumptions:
assert.sameValue("𝐁".length, 2, 'The length of "𝐁" is 2');
assert.sameValue("\u{1d401}".length, 2, 'The length of "\\u{1d401}" is 2');
assert.sameValue("\uD835\uDC01".length, 2, 'The length of "\\uD835\\uDC01" is 2');
assert.sameValue(2, "𝐁".match(/./u)[0].length, 'The length of a single code point match against "𝐁" is 2 (with /u flag)');
assert.sameValue(2, "\u{1d401}".match(/./u)[0].length, 'The length of a single code point match against "\\u{1d401}" is 2 (with /u flag)');
assert.sameValue(2, "\uD835\uDC01".match(/./u)[0].length, 'The length of a single code point match against "\\ud835\\udc01" is 2 (with /u flag)');
assert.compareArray([0, 2], "𝐁".match(/./u).indices[0], 'Indices for unicode match against "𝐁" (with /u flag)');
assert.compareArray([0, 2], "\u{1d401}".match(/./u).indices[0], 'Indices for unicode match against \\u{1d401} (with /u flag)');
assert.compareArray([0, 2], "\uD835\uDC01".match(/./u).indices[0], 'Indices for unicode match against \\ud835\\udc01 (with /u flag)');
assert.compareArray([0, 2], "𝐁".match(/(?<a>.)/u).indices.groups.a, 'Indices for unicode match against 𝐁 in groups.a (with /u flag)');
assert.compareArray([0, 2], "\u{1d401}".match(/(?<a>.)/u).indices.groups.a, 'Indices for unicode match against \\u{1d401} in groups.a (with /u flag)');
assert.compareArray([0, 2], "\uD835\uDC01".match(/(?<a>.)/u).indices.groups.a, 'Indices for unicode match against \\ud835\\udc01 in groups.a (with /u flag)');

View File

@ -0,0 +1,23 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Basic matching cases with non-unicode matches.
includes: [compareArray.js]
esid: sec-makeindicesarray
features: [regexp-match-indices]
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
assert.compareArray([1, 2], /(?<π>a)/u.exec("bab").indices.groups.π);
assert.compareArray([1, 2], /(?<\u{03C0}>a)/u.exec("bab").indices.groups.π);
assert.compareArray([1, 2], /(?<π>a)/u.exec("bab").indices.groups.\u03C0);
assert.compareArray([1, 2], /(?<\u{03C0}>a)/u.exec("bab").indices.groups.\u03C0);
assert.compareArray([1, 2], /(?<$>a)/u.exec("bab").indices.groups.$);
assert.compareArray([1, 2], /(?<_>a)/u.exec("bab").indices.groups._);
assert.compareArray([1, 2], /(?<$𐒤>a)/u.exec("bab").indices.groups.$𐒤);
assert.compareArray([1, 2], /(?<_\u200C>a)/u.exec("bab").indices.groups._\u200C);
assert.compareArray([1, 2], /(?<_\u200D>a)/u.exec("bab").indices.groups._\u200D);
assert.compareArray([1, 2], /(?<ಠ_ಠ>a)/u.exec("bab").indices.groups._ಠ);

View File

@ -0,0 +1,34 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: An unmatched capture in a match corresponds to an unmatched capture in "indices"
esid: sec-makeindicesarray
features: [regexp-match-indices]
info: |
MakeIndicesArray ( S, indices, groupNames )
4. Let _n_ be the number of elements in _indices_.
...
6. Set _A_ to ! ArrayCreate(_n_).
...
11. For each integer _i_ such that _i_ >= 0 and _i_ < _n_, do
a. Let _matchIndices_ be _indices_[_i_].
b. If _matchIndices_ is not *undefined*, then
i. Let _matchIndicesArray_ be ! GetMatchIndicesArray(_S_, _matchIndices_).
c. Else,
i. Let _matchIndicesArray_ be *undefined*.
d. Perform ! CreateDataProperty(_A_, ! ToString(_n_), _matchIndicesArray_).
...
---*/
/// <reference path="../../../../harness/assert.js" />
let input = "abd";
let match = /b(c)?/.exec(input);
let indices = match.indices;
// `indices` has the same length as match
assert.sameValue(indices.length, match.length);
// The second element of `indices` should be undefined.
assert.sameValue(indices[1], undefined);

View File

@ -0,0 +1,20 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: The "indices" property is an Array.
esid: sec-makeindicesarray
features: [regexp-match-indices]
info: |
MakeIndicesArray ( S, indices, groupNames )
6. Set _A_ to ! ArrayCreate(_n_).
---*/
/// <reference path="../../../../harness/assert.js" />
let match = /a/.exec("a");
let indices = match.indices;
// `indices` is an array
assert.sameValue(Object.getPrototypeOf(indices), Array.prototype);
assert(Array.isArray(indices));

View File

@ -0,0 +1,28 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: The groups object of indices is created unconditionally.
includes: [propertyHelper.js]
esid: sec-makeindicesarray
features: [regexp-named-groups]
info: |
MakeIndicesArray ( S, indices, groupNames )
8. If _groupNames_ is not *undefined*, then
a. Let _groups_ be ! ObjectCreate(*null*).
9. Else,
a. Let _groups_ be *undefined*.
10. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
const re = /./;
const indices = re.exec("a").indices;
verifyProperty(indices, 'groups', {
writable: true,
enumerable: true,
configurable: true,
value: undefined
});

View File

@ -0,0 +1,22 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Test the groups object of indices with matched and unmatched named captures.
includes: [compareArray.js]
esid: sec-makeindicesarray
features: [regexp-named-groups]
info: |
MakeIndicesArray ( S, indices, groupNames )
11. For each integer _i_ such that _i_ >= 0 and _i_ < _n_, do
e. If _groupNames_ is not *undfined* and _groupNames_[_i_] is not *undefined*, then
i. Perform ! CreateDataProperty(_groups_, _groupNames_[_i_], _matchIndicesArray_).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
const re = /(?<a>a).|(?<x>x)/;
const result = re.exec("ab").indices;
assert.compareArray([0, 1], result.groups.a);
assert.sameValue(undefined, result.groups.x);

View File

@ -0,0 +1,43 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: The groups object of indices is created with CreateDataProperty
includes: [compareArray.js, propertyHelper.js]
esid: sec-makeindicesarray
features: [regexp-named-groups]
info: |
MakeIndicesArray ( S, indices, groupNames )
8. If _groupNames_ is not *undefined*, then
a. Let _groups_ be ! ObjectCreate(*null*).
9. Else,
a. Let _groups_ be *undefined*.
10. Perform ! CreateDataProperty(_A_, `"groups"`, _groups_).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
// `groups` is created with Define, not Set.
let counter = 0;
Object.defineProperty(Array.prototype, "groups", {
set() { counter++; }
});
let indices = /(?<x>.)/.exec("a").indices;
assert.sameValue(counter, 0);
// `groups` is writable, enumerable and configurable
// (from CreateDataProperty).
verifyProperty(indices, 'groups', {
writable: true,
enumerable: true,
configurable: true
});
// The `__proto__` property on the groups object is not special,
// and does not affect the [[Prototype]] of the resulting groups object.
let {groups} = /(?<__proto__>.)/.exec("a").indices;
assert.compareArray([0, 1], groups.__proto__);
assert.sameValue(null, Object.getPrototypeOf(groups));

View File

@ -0,0 +1,38 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Properties of the groups object of indices are created with CreateDataProperty
includes: [compareArray.js, propertyHelper.js]
esid: sec-makeindicesarray
features: [regexp-named-groups]
info: |
MakeIndicesArray ( S, indices, groupNames )
11. For each integer _i_ such that _i_ >= 0 and _i_ < _n_, do
e. If _groupNames_ is not *undfined* and _groupNames_[_i_] is not *undefined*, then
i. Perform ! CreateDataProperty(_groups_, _groupNames_[_i_], _matchIndicesArray_).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/compareArray.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
// Properties created on result.groups in textual order.
let groupNames = Object.getOwnPropertyNames(/(?<fst>.)|(?<snd>.)/u.exec("abcd").indices.groups);
assert.compareArray(groupNames, ["fst", "snd"]);
// // Properties are created with Define, not Set
// let counter = 0;
// Object.defineProperty(Object.prototype, 'x', {set() { counter++; }});
let indices = /(?<x>.)/.exec('a').indices;
let groups = indices.groups;
// assert.sameValue(counter, 0);
// Properties are writable, enumerable and configurable
// (from CreateDataProperty)
verifyProperty(groups, 'x', {
writable: true,
enumerable: true,
configurable: true
});

View File

@ -0,0 +1,32 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: The "indices" property is created with DefinePropertyOrThrow
includes: [propertyHelper.js]
esid: sec-regexpbuiltinexec
features: [regexp-match-indices]
info: |
Runtime Semantics: RegExpBuiltinExec ( R, S )
34. Let _indicesArray_ be MakeIndicesArray(_S_, _indices_, _groupNames_).
35. Perform ! DefinePropertyOrThrow(_A_, `"indices"`, PropertyDescriptor { [[Value]]: _indicesArray_, [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }).
---*/
/// <reference path="../../../../harness/assert.js" />
/// <reference path="../../../../harness/propertyHelper.js" />
// `indices` is created with Define, not Set.
let counter = 0;
Object.defineProperty(Array.prototype, "indices", {
set() { counter++; }
});
let match = /a/.exec("a");
assert.sameValue(counter, 0);
// `indices` is a non-writable, non-enumerable, and configurable data-property.
verifyProperty(match, 'indices', {
writable: true,
enumerable: true,
configurable: true
});

View File

@ -0,0 +1,17 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
array values compare correctly.
includes: [deepEqual.js]
---*/
/// <reference path="../../harness/assert.js" />
/// <reference path="../../harness/deepEqual.js" />
assert.deepEqual([], []);
assert.deepEqual([1, "a", true], [1, "a", true]);
assert.throws(Test262Error, function () { assert.deepEqual([], [1]); });
assert.throws(Test262Error, function () { assert.deepEqual([1, "a", true], [1, "a", false]); });

View File

@ -0,0 +1,20 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
values compare correctly with circular references.
includes: [deepEqual.js]
---*/
/// <reference path="../../harness/assert.js" />
/// <reference path="../../harness/deepEqual.js" />
var a = { x: 1 };
var b = { x: 1 };
a.a = a;
a.b = b;
b.a = b;
b.b = a;
assert.deepEqual(a, b);

View File

@ -0,0 +1,16 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
values compare correctly.
includes: [deepEqual.js]
---*/
/// <reference path="../../harness/assert.js" />
/// <reference path="../../harness/deepEqual.js" />
assert.deepEqual({ a: { x: 1 }, b: [true] }, { a: { x: 1 }, b: [true] });
assert.throws(Test262Error, function () { assert.deepEqual({}, { a: { x: 1 }, b: [true] }); });
assert.throws(Test262Error, function () { assert.deepEqual({ a: { x: 1 }, b: [true] }, { a: { x: 1 }, b: [false] }); });

View File

@ -0,0 +1,22 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
map/set values compare correctly.
includes: [deepEqual.js]
---*/
/// <reference path="../../harness/assert.js" />
/// <reference path="../../harness/deepEqual.js" />
assert.deepEqual(new Set(), new Set());
assert.deepEqual(new Set([1, "a", true]), new Set([1, "a", true]));
assert.deepEqual(new Map(), new Map());
assert.deepEqual(new Map([[1, "a"], ["b", true]]), new Map([[1, "a"], ["b", true]]));
assert.throws(Test262Error, function () { assert.deepEqual(new Set([]), new Set([1])); });
assert.throws(Test262Error, function () { assert.deepEqual(new Set([1, "a", true]), new Set([1, "a", false])); });
assert.throws(Test262Error, function () { assert.deepEqual(new Map([]), new Map([[1, "a"], ["b", true]])); });
assert.throws(Test262Error, function () { assert.deepEqual(new Map([[1, "a"], ["b", true]]), new Map([[1, "a"], ["b", false]])); });
assert.throws(Test262Error, function () { assert.deepEqual(new Map([[1, "a"], ["b", true]]), new Set([[1, "a"], ["b", false]])); });

View File

@ -0,0 +1,17 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
object values compare correctly.
includes: [deepEqual.js]
---*/
/// <reference path="../../harness/assert.js" />
/// <reference path="../../harness/deepEqual.js" />
assert.deepEqual({}, {});
assert.deepEqual({ a: 1, b: true }, { a: 1, b: true });
assert.throws(Test262Error, function () { assert.deepEqual({}, { a: 1, b: true }); });
assert.throws(Test262Error, function () { assert.deepEqual({ a: 1, b: true }, { a: 1, b: false }); });

View File

@ -0,0 +1,38 @@
// Copyright 2019 Ron Buckton. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
primitive values compare correctly.
includes: [deepEqual.js]
---*/
/// <reference path="../../harness/assert.js" />
/// <reference path="../../harness/deepEqual.js" />
var s1 = Symbol();
var s2 = Symbol();
assert.deepEqual(null, null);
assert.deepEqual(undefined, undefined);
assert.deepEqual("a", "a");
assert.deepEqual(1, 1);
assert.deepEqual(1n, 1n);
assert.deepEqual(true, true);
assert.deepEqual(s1, s1);
assert.deepEqual(Object("a"), "a");
assert.deepEqual(Object(1), 1);
assert.deepEqual(Object(1n), 1n);
assert.deepEqual(Object(true), true);
assert.deepEqual(Object(s1), s1);
assert.throws(Test262Error, function () { assert.deepEqual(null, 0); });
assert.throws(Test262Error, function () { assert.deepEqual(undefined, 0); });
assert.throws(Test262Error, function () { assert.deepEqual("", 0); });
assert.throws(Test262Error, function () { assert.deepEqual("1", 1); });
assert.throws(Test262Error, function () { assert.deepEqual("1", "2"); });
assert.throws(Test262Error, function () { assert.deepEqual(1n, 1); });
assert.throws(Test262Error, function () { assert.deepEqual(1n, 2n); });
assert.throws(Test262Error, function () { assert.deepEqual(true, 1); });
assert.throws(Test262Error, function () { assert.deepEqual(true, false); });
assert.throws(Test262Error, function () { assert.deepEqual(s1, "Symbol()"); });
assert.throws(Test262Error, function () { assert.deepEqual(s1, s2); });