Skip to content

Commit

Permalink
document and use the changes regarding regexp done in core-js
Browse files Browse the repository at this point in the history
  • Loading branch information
rbri committed Jan 5, 2024
1 parent 3fa4705 commit 2ffc731
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 30 deletions.
13 changes: 13 additions & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@

<body>
<release version="3.10.0" date="December xx, 2023" description="Chrome/Edge 120, Firefox 120, Bugfixes">
<action type="add" dev="Lai Quang Duong">
String.includes/startsWith/endsWith no longer throws a TypeError when the first argument is a regex
and Symbol.match of that regex has been set to false.
</action>
<action type="add" dev="Lai Quang Duong">
String.prototype.replaceAll implemented.
</action>
<action type="add" dev="Lai Quang Duong">
RegExp.dotAll flag implemented.
</action>
<action type="fix" dev="Lai Quang Duong">
No longer throw an TypeError when RegExp.prototype.toString is called on a NativeObject.
</action>
<action type="add" dev="rbri">
Symbol.iterator property is now available on CSSStyleDeclaration/ComputedCSSStyleDeclaration.
</action>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.htmlunit.BrowserVersion;
import org.htmlunit.NotYetImplementedException;
import org.htmlunit.corejs.javascript.Context;
import org.htmlunit.corejs.javascript.RegExpProxy;
import org.htmlunit.corejs.javascript.ScriptRuntime;
Expand Down Expand Up @@ -92,21 +93,30 @@ public Object action(final Context cx, final Scriptable scope, final Scriptable
private Object doAction(final Context cx, final Scriptable scope, final Scriptable thisObj,
final Object[] args, final int actionType) {
// in a first time just improve replacement with a String (not a function)
if (RA_REPLACE == actionType && args.length == 2 && args[1] instanceof String) {
if ((RA_REPLACE == actionType || RA_REPLACE_ALL == actionType)
&& args.length == 2 && args[1] instanceof String) {
final String thisString = JavaScriptEngine.toString(thisObj);
final String replacement = (String) args[1];
final Object arg0 = args[0];
if (arg0 instanceof String) {
// arg0 should *not* be interpreted as a RegExp
return doStringReplacement(thisString, (String) arg0, replacement);
return doStringReplacement(thisString, (String) arg0, replacement, RA_REPLACE_ALL == actionType);
}

if (arg0 instanceof NativeRegExp) {
try {
final NativeRegExp regexp = (NativeRegExp) arg0;

if (RA_REPLACE_ALL == actionType
&& (regexp.getFlags() & NativeRegExp.JSREG_GLOB) == 0) {
throw ScriptRuntime.typeError(
"replaceAll must be called with a global RegExp");
}

final RegExpData reData = new RegExpData(regexp);
final Matcher matcher = reData.getPattern().matcher(thisString);
return doReplacement(thisString, replacement, matcher, reData.isGlobal());
return doReplacement(thisString, replacement, matcher,
reData.isGlobal() || RA_REPLACE_ALL == actionType);
}
catch (final PatternSyntaxException e) {
LOG.warn(e.getMessage(), e);
Expand Down Expand Up @@ -174,25 +184,33 @@ else if (RA_MATCH == actionType || RA_SEARCH == actionType) {
}

private String doStringReplacement(final String originalString,
final String searchString, final String replacement) {
final String searchString, final String replacement,
final boolean replaceAll) {
if (originalString == null) {
return "";
}

final StaticStringMatcher matcher = new StaticStringMatcher(originalString, searchString);
if (matcher.start() > -1) {
final StringBuilder sb = new StringBuilder()
.append(originalString, 0, matcher.start_);

final StringBuilder sb = new StringBuilder();
int previousIndex = 0;

while (matcher.find()) {
sb.append(originalString, previousIndex, matcher.start());

String localReplacement = replacement;
if (replacement.contains("$")) {
localReplacement = computeReplacementValue(localReplacement, originalString, matcher, false);
}
sb.append(localReplacement)
.append(originalString, matcher.end_, originalString.length());
return sb.toString();
sb.append(localReplacement);
previousIndex = matcher.end();

if (!replaceAll) {
break;
}
}
return originalString;
sb.append(originalString, previousIndex, originalString.length());
return sb.toString();
}

private String doReplacement(final String originalString, final String replacement, final Matcher matcher,
Expand Down Expand Up @@ -487,20 +505,40 @@ static String jsRegExpToJavaRegExp(final String re) {
* Simple helper.
*/
private static final class StaticStringMatcher implements MatchResult {
private final String group_;
private final int start_;
private final int end_;
private final String original_;
private final String search_;

private int start_;
private int end_;

StaticStringMatcher(final String originalString, final String searchString) {
final int pos = originalString.indexOf(searchString);
group_ = searchString;
start_ = pos;
end_ = pos + searchString.length();
original_ = originalString;
search_ = searchString;

start_ = -1;
end_ = 0;
}

public boolean find() {
if (start_ == end_) {
end_++;
}
if (end_ > original_.length()) {
return false;
}

final int pos = original_.indexOf(search_, end_);
if (pos != -1) {
start_ = pos;
end_ = pos + search_.length();
return true;
}
return false;
}

@Override
public String group() {
return group_;
return search_;
}

@Override
Expand All @@ -515,25 +553,21 @@ public int end() {

@Override
public int start(final int group) {
// not used so far
return 0;
throw new NotYetImplementedException("StaticStringMatcher.start(int)");
}

@Override
public int end(final int group) {
// not used so far
return 0;
throw new NotYetImplementedException("StaticStringMatcher.end(int)");
}

@Override
public String group(final int group) {
// not used so far
return null;
throw new NotYetImplementedException("StaticStringMatcher.group(int)");
}

@Override
public int groupCount() {
// not used so far
return 0;
}
}
Expand Down
93 changes: 93 additions & 0 deletions src/test/java/org/htmlunit/javascript/NativeStringTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,97 @@ public void toLocaleLowerCase() throws Exception {

loadPageVerifyTitle2(html);
}

/**
* @throws Exception if something goes wrong
*/
@Test
@Alerts(DEFAULT = {"Error", "true"},
IE = {})
public void includesRegExpMatch() throws Exception {
final String html
= "<!DOCTYPE html>\n"
+ "<html><head><script>\n"
+ LOG_TITLE_FUNCTION
+ " function doTest() {\n"
+ " if (window.Symbol) {\n"
+ " var regExp = /./;\n"
+ " var res = '';\n"
+ " try {\n"
+ " log('/./'.includes(regExp));\n"
+ " } catch (e) {\n"
+ " log('Error');\n"
+ " }\n"
+ " regExp[Symbol.match] = false;\n"
+ " log('/./'.includes(regExp));\n"
+ " }\n"
+ " }\n"
+ "</script></head>"
+ "<body onload='doTest()'>\n"
+ "</body></html>";

loadPageVerifyTitle2(html);
}

/**
* @throws Exception if something goes wrong
*/
@Test
@Alerts(DEFAULT = {"Error", "true"},
IE = {})
public void startsWithRegExpMatch() throws Exception {
final String html
= "<!DOCTYPE html>\n"
+ "<html><head><script>\n"
+ LOG_TITLE_FUNCTION
+ " function doTest() {\n"
+ " if (window.Symbol) {\n"
+ " var regExp = /./;\n"
+ " var res = '';\n"
+ " try {\n"
+ " log('/./'.startsWith(regExp));\n"
+ " } catch (e) {\n"
+ " log('Error');\n"
+ " }\n"
+ " regExp[Symbol.match] = false;\n"
+ " log('/./'.startsWith(regExp));\n"
+ " }\n"
+ " }\n"
+ "</script></head>"
+ "<body onload='doTest()'>\n"
+ "</body></html>";

loadPageVerifyTitle2(html);
}

/**
* @throws Exception if something goes wrong
*/
@Test
@Alerts(DEFAULT = {"Error", "true"},
IE = {})
public void endsWithRegExpMatch() throws Exception {
final String html
= "<!DOCTYPE html>\n"
+ "<html><head><script>\n"
+ LOG_TITLE_FUNCTION
+ " function doTest() {\n"
+ " if (window.Symbol) {\n"
+ " var regExp = /./;\n"
+ " var res = '';\n"
+ " try {\n"
+ " log('/./'.endsWith(regExp));\n"
+ " } catch (e) {\n"
+ " log('Error');\n"
+ " }\n"
+ " regExp[Symbol.match] = false;\n"
+ " log('/./'.endsWith(regExp));\n"
+ " }\n"
+ " }\n"
+ "</script></head>"
+ "<body onload='doTest()'>\n"
+ "</body></html>";

loadPageVerifyTitle2(html);
}
}
Loading

0 comments on commit 2ffc731

Please sign in to comment.