Skip to content

Commit 5ae7de9

Browse files
authored
Increase coverage of String.prototype.replace $xy replacement patterns (#3931)
* $xy is a valid capture index * $xy is not a valid capture index but $x is * neither $xy nor $x is a valid capture index
1 parent 954d637 commit 5ae7de9

File tree

1 file changed

+230
-0
lines changed

1 file changed

+230
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Copyright (C) 2023 Richard Gibson. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
/*---
4+
esid: sec-string.prototype.replace
5+
description: >
6+
All $n and $nn substrings of the replacement template must be replaced with
7+
contents of the corresponding regular expression match capture group (if the
8+
n or nn identifies a valid capture index).
9+
info: |
10+
String.prototype.replace ( searchValue, replaceValue )
11+
12+
1. Let O be ? RequireObjectCoercible(this value).
13+
2. If searchValue is neither undefined nor null, then
14+
a. Let replacer be ? GetMethod(searchValue, @@replace).
15+
b. If replacer is not undefined, then
16+
i. Return ? Call(replacer, searchValue, « O, replaceValue »).
17+
18+
RegExp.prototype [ @@replace ] ( string, replaceValue )
19+
20+
15. For each element result of results, do
21+
a. Let resultLength be ? LengthOfArrayLike(result).
22+
b. Let nCaptures be max(resultLength - 1, 0).
23+
c. Let matched be ? ToString(? Get(result, "0")).
24+
d. Let matchLength be the length of matched.
25+
e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")).
26+
f. Set position to the result of clamping position between 0 and lengthS.
27+
g. Let captures be a new empty List.
28+
h. Let n be 1.
29+
i. Repeat, while n ≤ nCaptures,
30+
...
31+
iii. Append capN to captures.
32+
iv. NOTE: When n = 1, the preceding step puts the first element into captures (at index 0). More generally, the nth capture (the characters captured by the nth set of capturing parentheses) is at captures[n - 1].
33+
v. Set n to n + 1.
34+
j. Let namedCaptures be ? Get(result, "groups").
35+
k. If functionalReplace is true, then
36+
...
37+
l. Else,
38+
i. If namedCaptures is not undefined, then
39+
1. Set namedCaptures to ? ToObject(namedCaptures).
40+
ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue).
41+
...
42+
16. If nextSourcePosition ≥ lengthS, return accumulatedResult.
43+
17. Return the string-concatenation of accumulatedResult and the substring of S from nextSourcePosition.
44+
45+
GetSubstitution ( matched, str, position, captures, namedCaptures, replacementTemplate )
46+
47+
1. Let stringLength be the length of str.
48+
2. Assert: position ≤ stringLength.
49+
3. Let result be the empty String.
50+
4. Let templateRemainder be replacementTemplate.
51+
5. Repeat, while templateRemainder is not the empty String,
52+
a. NOTE: The following steps isolate ref (a prefix of templateRemainder), determine refReplacement (its replacement), and then append that replacement to result.
53+
...
54+
f. Else if templateRemainder starts with "$" followed by 1 or more decimal digits, then
55+
i. If templateRemainder starts with "$" followed by 2 or more decimal digits, let digitCount be 2. Otherwise, let digitCount be 1.
56+
ii. Let digits be the substring of templateRemainder from 1 to 1 + digitCount.
57+
iii. Let index be ℝ(StringToNumber(digits)).
58+
iv. Assert: 0 ≤ index ≤ 99.
59+
v. Let captureLen be the number of elements in captures.
60+
vi. If index > captureLen and digitCount > 1, then
61+
1. NOTE: When a two-digit replacement pattern specifies an index exceeding the count of capturing groups, it is treated as a one-digit replacement pattern followed by a literal digit.
62+
2. Set digitCount to 1.
63+
3. Set digits to the substring of digits from 0 to 1.
64+
4. Set index to ℝ(StringToNumber(digits)).
65+
vii. Let ref be the substring of templateRemainder from 0 to 1 + digitCount.
66+
viii. If 1 ≤ index ≤ captureLen, then
67+
1. Let capture be captures[index - 1].
68+
2. If capture is undefined, then
69+
a. Let refReplacement be the empty String.
70+
3. Else,
71+
a. Let refReplacement be capture.
72+
ix. Else,
73+
1. Let refReplacement be ref.
74+
...
75+
i. Let refLength be the length of ref.
76+
j. Set templateRemainder to the substring of templateRemainder from refLength.
77+
k. Set result to the string-concatenation of result and refReplacement.
78+
6. Return result.
79+
---*/
80+
81+
var str = 'foo-x-bar';
82+
83+
var x = 'x';
84+
var re0 = /x/;
85+
var re1 = /(x)/;
86+
var re1x = /(x)($^)?/;
87+
var re10 = /((((((((((x))))))))))/;
88+
89+
assert.sameValue(str.replace(x, '|$0|'), 'foo-|$0|-bar',
90+
'`$0` is not a capture index for string "' + x + '"');
91+
assert.sameValue(str.replace(re0, '|$0|'), 'foo-|$0|-bar',
92+
'`$0` is not a capture index in ' + String(re0));
93+
assert.sameValue(str.replace(re1, '|$0|'), 'foo-|$0|-bar',
94+
'`$0` is not a capture index in ' + String(re1));
95+
assert.sameValue(str.replace(re1x, '|$0|'), 'foo-|$0|-bar',
96+
'`$0` is not a capture index in ' + String(re1x));
97+
assert.sameValue(str.replace(re10, '|$0|'), 'foo-|$0|-bar',
98+
'`$0` is not a capture index in ' + String(re10));
99+
100+
assert.sameValue(str.replace(x, '|$00|'), 'foo-|$00|-bar',
101+
'`$00` is not a capture index for string "' + x + '"');
102+
assert.sameValue(str.replace(re0, '|$00|'), 'foo-|$00|-bar',
103+
'`$00` is not a capture index in ' + String(re0));
104+
assert.sameValue(str.replace(re1, '|$00|'), 'foo-|$00|-bar',
105+
'`$00` is not a capture index in ' + String(re1));
106+
assert.sameValue(str.replace(re1x, '|$00|'), 'foo-|$00|-bar',
107+
'`$00` is not a capture index in ' + String(re1x));
108+
assert.sameValue(str.replace(re10, '|$00|'), 'foo-|$00|-bar',
109+
'`$00` is not a capture index in ' + String(re10));
110+
111+
assert.sameValue(str.replace(x, '|$000|'), 'foo-|$000|-bar',
112+
'`$00` before `0` is not a capture index for string "' + x + '"');
113+
assert.sameValue(str.replace(re0, '|$000|'), 'foo-|$000|-bar',
114+
'`$00` before `0` is not a capture index in ' + String(re0));
115+
assert.sameValue(str.replace(re1, '|$000|'), 'foo-|$000|-bar',
116+
'`$00` before `0` is not a capture index in ' + String(re1));
117+
assert.sameValue(str.replace(re1x, '|$000|'), 'foo-|$000|-bar',
118+
'`$00` before `0` is not a capture index in ' + String(re1x));
119+
assert.sameValue(str.replace(re10, '|$000|'), 'foo-|$000|-bar',
120+
'`$00` before `0` is not a capture index in ' + String(re10));
121+
122+
assert.sameValue(str.replace(x, '|$1|'), 'foo-|$1|-bar',
123+
'`$1` is not a capture index for string "' + x + '"');
124+
assert.sameValue(str.replace(re0, '|$1|'), 'foo-|$1|-bar',
125+
'`$1` is not a capture index in ' + String(re0));
126+
assert.sameValue(str.replace(re1, '|$1|'), 'foo-|x|-bar',
127+
'`$1` is a capture index in ' + String(re1));
128+
assert.sameValue(str.replace(re1x, '|$1|'), 'foo-|x|-bar',
129+
'`$1` is a capture index in ' + String(re1x));
130+
assert.sameValue(str.replace(re10, '|$1|'), 'foo-|x|-bar',
131+
'`$1` is a capture index in ' + String(re10));
132+
133+
assert.sameValue(str.replace(x, '|$01|'), 'foo-|$01|-bar',
134+
'`$01` is not a capture index for string "' + x + '"');
135+
assert.sameValue(str.replace(re0, '|$01|'), 'foo-|$01|-bar',
136+
'`$01` is not a capture index in ' + String(re0));
137+
assert.sameValue(str.replace(re1, '|$01|'), 'foo-|x|-bar',
138+
'`$01` is a capture index in ' + String(re1));
139+
assert.sameValue(str.replace(re1x, '|$01|'), 'foo-|x|-bar',
140+
'`$01` is a capture index in ' + String(re1x));
141+
assert.sameValue(str.replace(re10, '|$01|'), 'foo-|x|-bar',
142+
'`$01` is a capture index in ' + String(re10));
143+
144+
assert.sameValue(str.replace(x, '|$010|'), 'foo-|$010|-bar',
145+
'`$01` before `0` is not a capture index for string "' + x + '"');
146+
assert.sameValue(str.replace(re0, '|$010|'), 'foo-|$010|-bar',
147+
'`$01` before `0` is not a capture index in ' + String(re0));
148+
assert.sameValue(str.replace(re1, '|$010|'), 'foo-|x0|-bar',
149+
'`$01` before `0` is a capture index in ' + String(re1));
150+
assert.sameValue(str.replace(re1x, '|$010|'), 'foo-|x0|-bar',
151+
'`$01` before `0` is a capture index in ' + String(re1x));
152+
assert.sameValue(str.replace(re10, '|$010|'), 'foo-|x0|-bar',
153+
'`$01` before `0` is a capture index in ' + String(re10));
154+
155+
assert.sameValue(str.replace(x, '|$2|'), 'foo-|$2|-bar',
156+
'`$2` is not a capture index for string "' + x + '"');
157+
assert.sameValue(str.replace(re0, '|$2|'), 'foo-|$2|-bar',
158+
'`$2` is not a capture index in ' + String(re0));
159+
assert.sameValue(str.replace(re1, '|$2|'), 'foo-|$2|-bar',
160+
'`$2` is not a capture index in ' + String(re1));
161+
assert.sameValue(str.replace(re1x, '|$2|'), 'foo-||-bar',
162+
'`$2` is a failed capture index in ' + String(re1x));
163+
assert.sameValue(str.replace(re10, '|$2|'), 'foo-|x|-bar',
164+
'`$2` is a capture index in ' + String(re10));
165+
166+
assert.sameValue(str.replace(x, '|$02|'), 'foo-|$02|-bar',
167+
'`$02` is not a capture index for string "' + x + '"');
168+
assert.sameValue(str.replace(re0, '|$02|'), 'foo-|$02|-bar',
169+
'`$02` is not a capture index in ' + String(re0));
170+
assert.sameValue(str.replace(re1, '|$02|'), 'foo-|$02|-bar',
171+
'`$02` is not a capture index in ' + String(re1));
172+
assert.sameValue(str.replace(re1x, '|$02|'), 'foo-||-bar',
173+
'`$02` is a failed capture index in ' + String(re1x));
174+
assert.sameValue(str.replace(re10, '|$02|'), 'foo-|x|-bar',
175+
'`$02` is a capture index in ' + String(re10));
176+
177+
assert.sameValue(str.replace(x, '|$020|'), 'foo-|$020|-bar',
178+
'`$02` before `0` is not a capture index for string "' + x + '"');
179+
assert.sameValue(str.replace(re0, '|$020|'), 'foo-|$020|-bar',
180+
'`$02` before `0` is not a capture index in ' + String(re0));
181+
assert.sameValue(str.replace(re1, '|$020|'), 'foo-|$020|-bar',
182+
'`$02` before `0` is not a capture index in ' + String(re1));
183+
assert.sameValue(str.replace(re1x, '|$020|'), 'foo-|0|-bar',
184+
'`$02` before `0` is a failed capture index in ' + String(re1x));
185+
assert.sameValue(str.replace(re10, '|$020|'), 'foo-|x0|-bar',
186+
'`$02` before `0` is a capture index in ' + String(re10));
187+
188+
assert.sameValue(str.replace(x, '|$10|'), 'foo-|$10|-bar',
189+
'`$10` is not a capture index (nor is `$1`) for string "' + x + '"');
190+
assert.sameValue(str.replace(re0, '|$10|'), 'foo-|$10|-bar',
191+
'`$10` is not a capture index (nor is `$1`) in ' + String(re0));
192+
assert.sameValue(str.replace(re1, '|$10|'), 'foo-|x0|-bar',
193+
'`$10` is not a capture index (but `$1` is) in ' + String(re1));
194+
assert.sameValue(str.replace(re1x, '|$10|'), 'foo-|x0|-bar',
195+
'`$10` is not a capture index (but `$1` is) in ' + String(re1x));
196+
assert.sameValue(str.replace(re10, '|$10|'), 'foo-|x|-bar',
197+
'`$10` is a capture index in ' + String(re10));
198+
199+
assert.sameValue(str.replace(x, '|$100|'), 'foo-|$100|-bar',
200+
'`$10` before `0` is not a capture index (nor is `$1`) for string "' + x + '"');
201+
assert.sameValue(str.replace(re0, '|$100|'), 'foo-|$100|-bar',
202+
'`$10` before `0` is not a capture index (nor is `$1`) in ' + String(re0));
203+
assert.sameValue(str.replace(re1, '|$100|'), 'foo-|x00|-bar',
204+
'`$10` before `0` is not a capture index (but `$1` is) in ' + String(re1));
205+
assert.sameValue(str.replace(re1x, '|$100|'), 'foo-|x00|-bar',
206+
'`$10` before `0` is not a capture index (but `$1` is) in ' + String(re1x));
207+
assert.sameValue(str.replace(re10, '|$100|'), 'foo-|x0|-bar',
208+
'`$10` before `0` is a capture index in ' + String(re10));
209+
210+
assert.sameValue(str.replace(x, '|$20|'), 'foo-|$20|-bar',
211+
'`$20` is not a capture index (nor is `$2`) for string "' + x + '"');
212+
assert.sameValue(str.replace(re0, '|$20|'), 'foo-|$20|-bar',
213+
'`$20` is not a capture index (nor is `$2`) in ' + String(re0));
214+
assert.sameValue(str.replace(re1, '|$20|'), 'foo-|$20|-bar',
215+
'`$20` is not a capture index (nor is `$2`) in ' + String(re1));
216+
assert.sameValue(str.replace(re1x, '|$20|'), 'foo-|0|-bar',
217+
'`$20` is not a capture index (but `$2` is a failed capture index) in ' + String(re1x));
218+
assert.sameValue(str.replace(re10, '|$20|'), 'foo-|x0|-bar',
219+
'`$20` is not a capture index (but `$2` is) in ' + String(re10));
220+
221+
assert.sameValue(str.replace(x, '|$200|'), 'foo-|$200|-bar',
222+
'`$20` before `0` is not a capture index (nor is `$2`) for string "' + x + '"');
223+
assert.sameValue(str.replace(re0, '|$200|'), 'foo-|$200|-bar',
224+
'`$20` before `0` is not a capture index (nor is `$2`) in ' + String(re0));
225+
assert.sameValue(str.replace(re1, '|$200|'), 'foo-|$200|-bar',
226+
'`$20` before `0` is not a capture index (nor is `$2`) in ' + String(re1));
227+
assert.sameValue(str.replace(re1x, '|$200|'), 'foo-|00|-bar',
228+
'`$20` before `0` is not a capture index (but `$2` is a failed capture index) in ' + String(re1x));
229+
assert.sameValue(str.replace(re10, '|$200|'), 'foo-|x00|-bar',
230+
'`$20` before `0` is not a capture index (but `$2` is) in ' + String(re10));

0 commit comments

Comments
 (0)