Skip to content

Commit d91bd1b

Browse files
concavelenzcopybara-github
authored andcommitted
Fold "String#replaceAll" when all the values involved are literals.
PiperOrigin-RevId: 383518491
1 parent 47f0369 commit d91bd1b

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ private Node tryFoldKnownStringMethods(Node subtree, Node callTarget) {
275275
return tryFoldStringCharAt(subtree, stringNode, firstArg);
276276
case "charCodeAt":
277277
return tryFoldStringCharCodeAt(subtree, stringNode, firstArg);
278+
case "replaceAll":
279+
return tryFoldStringReplaceAll(subtree, stringNode, firstArg);
278280
default: // fall out
279281
}
280282
}
@@ -859,6 +861,39 @@ private Node tryFoldStringCharAt(Node n, Node stringNode, Node arg1) {
859861
return resultNode;
860862
}
861863

864+
/** Try to fold .charAt() calls on strings */
865+
private Node tryFoldStringReplaceAll(Node n, Node stringNode, Node arg1) {
866+
checkArgument(n.isCall());
867+
checkArgument(stringNode.isStringLit());
868+
869+
Node arg2 = arg1.getNext();
870+
if (arg2 == null || arg2.getNext() != null) {
871+
// too few or too many parameters
872+
return n;
873+
}
874+
875+
if (!arg1.isStringLit() || !arg2.isStringLit()) {
876+
// only string literals are supported for folding.
877+
return n;
878+
}
879+
880+
String replacementPattern = arg2.getString();
881+
if (replacementPattern.contains("$")) {
882+
// 'special' replacements aren't supported yet.
883+
return n;
884+
}
885+
886+
// Java "replace" acts like JavaScript's "replaceAll" and replaces all occurrences.
887+
String original = stringNode.getString();
888+
String newString = original.replace(arg1.getString(), arg2.getString());
889+
890+
Node resultNode = IR.string(newString).srcref(stringNode);
891+
Node parent = n.getParent();
892+
n.replaceWith(resultNode);
893+
reportChangeToEnclosingScope(parent);
894+
return resultNode;
895+
}
896+
862897
/**
863898
* Try to fold .charCodeAt() calls on strings
864899
*/

test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ public PeepholeReplaceKnownMethodsTest() {
3737
super(
3838
MINIMAL_EXTERNS
3939
+ lines(
40-
// NOTE: these are defined as variadic to avoid wrong-argument-count warnings in
41-
// NTI,
40+
// NOTE: these are defined as variadic to avoid wrong-argument-count warnings,
4241
// which enables testing that the pass does not touch calls with wrong argument
4342
// count.
43+
"/** @type {function(this: string, ...*): string} */ String.prototype.replaceAll;",
44+
"/** @type {function(this: string, ...*): string} */ String.prototype.replace;",
4445
"/** @type {function(this: string, ...*): string} */ String.prototype.substring;",
4546
"/** @type {function(this: string, ...*): string} */ String.prototype.substr;",
4647
"/** @type {function(this: string, ...*): string} */ String.prototype.slice;",
@@ -205,6 +206,41 @@ public void testFoldStringSubstr() {
205206
foldSame("x = `abc ${xyz} def`.substr(0,2)");
206207
}
207208

209+
@Test
210+
public void testFoldStringReplace() {
211+
// TODO(johnlenz): support folding this case
212+
foldSame("x = 'acaca'.replace('c','x')");
213+
214+
// not a literal
215+
foldSame("x.replace('x','c')");
216+
217+
foldSame("'Xyz'.replace('Xyz', '$$')"); // would fold to '$'
218+
foldSame("'PreXyzPost'.replace('Xyz', '$&')"); // would fold to 'PreXyzPost'
219+
foldSame("'PreXyzPost'.replace('Xyz', '$`')"); // would fold to 'PrePrePost'
220+
foldSame("'PreXyzPost'.replace('Xyz', '$\\'')"); // would fold to 'PrePostPost'
221+
foldSame("'PreXyzPostXyz'.replace('Xyz', '$\\'')"); // would fold to 'PrePostXyzPost'
222+
foldSame("'123'.replace('2', '$`')"); // would fold to '113'
223+
}
224+
225+
@Test
226+
public void testFoldStringReplaceAll() {
227+
fold("x = 'abcde'.replaceAll('bcd','c')", "x = 'ace'");
228+
fold("x = 'abcde'.replaceAll('c','xxx')", "x = 'abxxxde'");
229+
fold("x = 'abcde'.replaceAll('xxx','c')", "x = 'abcde'");
230+
231+
fold("x = 'c_c_c'.replaceAll('c','x')", "x = 'x_x_x'");
232+
233+
// not a literal
234+
foldSame("x.replaceAll('x','c')");
235+
236+
foldSame("'Xyz'.replaceAll('Xyz', '$$')"); // would fold to '$'
237+
foldSame("'PreXyzPost'.replaceAll('Xyz', '$&')"); // would fold to 'PreXyzPost'
238+
foldSame("'PreXyzPost'.replaceAll('Xyz', '$`')"); // would fold to 'PrePrePost'
239+
foldSame("'PreXyzPost'.replaceAll('Xyz', '$\\'')"); // would fold to 'PrePostPost'
240+
foldSame("'PreXyzPostXyz'.replaceAll('Xyz', '$\\'')"); // would fold to 'PrePostXyzPost'
241+
foldSame("'123'.replaceAll('2', '$`')"); // would fold to '113'
242+
}
243+
208244
@Test
209245
public void testFoldStringSubstring() {
210246
fold("x = 'abcde'.substring(0,2)", "x = 'ab'");

0 commit comments

Comments
 (0)