Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve RegExp.prototype[@@replace] coverage #2481

Merged
merged 13 commits into from
Mar 23, 2020
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (C) 2020 Alexey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-regexp.prototype-@@replace
description: >
Abrupt completion during coercion of "lastIndex" property of `this` value.
info: |
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
11. Repeat, while done is false
a. Let result be ? RegExpExec(rx, S).
b. If result is null, set done to true.
c. Else,
i. Append result to the end of results.
ii. If global is false, set done to true.
iii. Else,
1. Let matchStr be ? ToString(? Get(result, "0")).
2. If matchStr is the empty String, then
a. Let thisIndex be ? ToLength(? Get(rx, "lastIndex")).
features: [Symbol.replace]
---*/

var r = /./g;
var execWasCalled = false;
var coercibleIndex = {
valueOf: function() {
throw new Test262Error();
},
};

var result = {
length: 1,
0: '',
index: 0,
};

r.exec = function() {
if (execWasCalled) {
return null;
}

r.lastIndex = coercibleIndex;
execWasCalled = true;
return result;
};

assert.throws(Test262Error, function() {
r[Symbol.replace]('', '');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (C) 2020 Alexey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-regexp.prototype-@@replace
description: >
Length coercion of "lastIndex" property of `this` value.
info: |
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
11. Repeat, while done is false
a. Let result be ? RegExpExec(rx, S).
b. If result is null, set done to true.
c. Else,
i. Append result to the end of results.
ii. If global is false, set done to true.
iii. Else,
1. Let matchStr be ? ToString(? Get(result, "0")).
2. If matchStr is the empty String, then
a. Let thisIndex be ? ToLength(? Get(rx, "lastIndex")).
b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
c. Perform ? Set(rx, "lastIndex", nextIndex, true).
features: [Symbol.replace]
---*/

var r = /./g;
var execWasCalled = false;
var coercibleIndex = {
valueOf: function() {
return Math.pow(2, 54);
},
};

var result = {
length: 1,
0: '',
index: 0,
};

r.exec = function() {
if (execWasCalled) {
return null;
}

r.lastIndex = coercibleIndex;
execWasCalled = true;
return result;
};

assert.sameValue(r[Symbol.replace]('', ''), '');
assert.sameValue(r.lastIndex, Math.pow(2, 53));
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@
// This code is governed by the BSD license found in the LICENSE file.

/*---
description: String coercion of the value returned by functional replaceValue
es6id: 21.2.5.8
esid: sec-regexp.prototype-@@replace
description: >
String coercion of the value returned by functional replaceValue.
info: |
16. Repeat, for each result in results,
[...]
m. If functionalReplace is true, then
i. Let replacerArgs be «matched».
ii. Append in list order the elements of captures to the end of the
List replacerArgs.
iii. Append position and S as the last two elements of replacerArgs.
iv. Let replValue be Call(replaceValue, undefined, replacerArgs).
v. Let replacement be ToString(replValue).
[...]
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
14. For each result in results, do
[...]
k. If functionalReplace is true, then
[...]
v. Let replValue be ? Call(replaceValue, undefined, replacerArgs).
vi. Let replacement be ? ToString(replValue).
features: [Symbol.replace]
---*/

var replacer = function() {
return {
toString: function() {
return 'toString value';
}
},
valueOf: function() {
throw new Test262Error('This method should not be invoked.');
},
};
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2020 Alexey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-regexp.prototype-@@replace
description: >
Arguments of functional replaceValue (`exec` result is empty array).
info: |
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
14. For each result in results, do
[...]
e. Let position be ? ToInteger(? Get(result, "index")).
[...]
k. If functionalReplace is true, then
i. Let replacerArgs be « matched ».
ii. Append in list order the elements of captures to the end of the List replacerArgs.
iii. Append position and S to replacerArgs.
[...]
v. Let replValue be ? Call(replaceValue, undefined, replacerArgs).
features: [Symbol.replace]
---*/

var args;
var replacer = function() {
args = arguments;
};

var r = /./;
r.exec = function() {
return [];
};

r[Symbol.replace]('foo', replacer);

assert.notSameValue(args, undefined);
assert.sameValue(args.length, 3);
assert.sameValue(args[0], 'undefined');
assert.sameValue(args[1], 0);
assert.sameValue(args[2], 'foo');
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (C) 2020 Alexey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-regexp.prototype-@@replace
description: >
Both functional and pattern replacement performs as expected with poisoned stdlib.
features: [Symbol.replace, regexp-named-groups]
---*/

assert(delete Array.prototype.concat);
assert(delete Array.prototype.push);
assert(delete Function.prototype.apply);
assert(delete String.prototype.charAt);
assert(delete String.prototype.charCodeAt);
assert(delete String.prototype.indexOf);
assert(delete String.prototype.slice);
assert(delete String.prototype.substring);

var str = "1a2";

assert.sameValue(/a/[Symbol.replace](str, "$`b"), "11b2");
assert.sameValue(/a/[Symbol.replace](str, "b$'"), "1b22");
assert.sameValue(/a/[Symbol.replace](str, "$3b$33"), "1$3b$332");
assert.sameValue(/(a)/[Symbol.replace](str, "$1b"), "1ab2");
assert.sameValue(/(?<a>a)/[Symbol.replace](str, "$<a>b"), "1ab2");

var replacer = function() {
return "b";
};

assert.sameValue(/a/[Symbol.replace](str, replacer), "1b2");
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,38 @@
// This code is governed by the BSD license found in the LICENSE file.

/*---
description: Type coercion of `1` property of result
es6id: 21.2.5.8
esid: sec-regexp.prototype-@@replace
description: >
String coercion of "3" property of the value returned by RegExpExec.
info: |
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
11. Repeat, while done is false
a. Let result be ? RegExpExec(rx, S).
[...]
13. Repeat, while done is false
a. Let result be RegExpExec(rx, S).
[...]
16. Repeat, for each result in results,
14. For each result in results, do
[...]
i. Repeat, while n ≤ nCaptures
i. Let capN be ? Get(result, ! ToString(n)).
ii. If capN is not undefined, then
1. Set capN to ? ToString(capN).
[...]
l. Repeat while n ≤ nCaptures
i. Let capN be Get(result, ToString(n)).
ii. ReturnIfAbrupt(capN).
iii. If capN is not undefined, then
1. Let capN be ToString(capN).
[...]
features: [Symbol.replace]
---*/

var r = /./;
var coercibleValue = {
length: 4,
index: 0,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was failing on JSC because it doesn't coerce index properly.

3: {
toString: function() {
return 'toString value';
}
}
},
valueOf: function() {
throw new Test262Error('This method should not be invoked.');
},
},
};
r.exec = function() {
return coercibleValue;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2020 Alexey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-regexp.prototype-@@replace
description: >
Abrupt completion during coercion of "groups"
property of the value returned by RegExpExec.
info: |
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
14. For each result in results, do
[...]
j. Let namedCaptures be ? Get(result, "groups").
k. If functionalReplace is true, then
[...]
l. Else,
i. If namedCaptures is not undefined, then
1. Set namedCaptures to ? ToObject(namedCaptures).
features: [Symbol.replace, regexp-named-groups]
---*/

var r = /./;
var coercibleValue = {
length: 1,
0: '',
index: 0,
groups: null,
};

r.exec = function() {
return coercibleValue;
};

assert.throws(TypeError, function() {
r[Symbol.replace]('bar', '');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2020 Alexey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-regexp.prototype-@@replace
description: >
Abrupt completion during coercion of value of "groups" object.
info: |
RegExp.prototype [ @@replace ] ( string, replaceValue )

[...]
14. For each result in results, do
[...]
j. Let namedCaptures be ? Get(result, "groups").
k. If functionalReplace is true, then
[...]
l. Else,
[...]
ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue).

Runtime Semantics: GetSubstitution ( matched, str, position, captures, namedCaptures, replacement )

[...]
11. Let result be the String value derived from replacement by copying code unit elements
from replacement to result while performing replacements as specified in Table 54.
These $ replacements are done left-to-right, and, once such a replacement is performed,
the new replacement text is not subject to further replacements.
12. Return result.

Table 54: Replacement Text Symbol Substitutions

$<

1. If namedCaptures is undefined, the replacement text is the String "$<".
2. Else,
a. Assert: Type(namedCaptures) is Object.
b. Scan until the next > U+003E (GREATER-THAN SIGN).
c. If none is found, the replacement text is the String "$<".
d. Else,
i. Let groupName be the enclosed substring.
ii. Let capture be ? Get(namedCaptures, groupName).
iii. If capture is undefined, replace the text through > with the empty String.
iv. Otherwise, replace the text through > with ? ToString(capture).
features: [Symbol.replace, regexp-named-groups]
---*/

var r = /./;
var coercibleValue = {
length: 1,
0: '',
index: 0,
groups: {
foo: {
toString: function() {
throw new Test262Error();
},
},
},
};

r.exec = function() {
return coercibleValue;
};

assert.throws(Test262Error, function() {
r[Symbol.replace]('a', '$<foo>');
});
Loading