aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2019-01-22 13:38:44 +0100
committerLars Knoll <lars.knoll@qt.io>2019-01-25 21:55:55 +0000
commitcf04d5ee18344d45da538810654690bd3936f46a (patch)
treef3d2f81f54865d2ce713407490d48bd0549c4e98
parentf4ab0ab62496e63db72592845b285bab3d8388b9 (diff)
Bring behavior of String.replace() in line with other engines
"x".replace("x", "$1") gives "$1" in both JSC and V8, as there are no captures that could be used as a replacement for $1. Implement the same behavior as it's the most logical thing to do (even though it's undefined according to the spec). Two digit captures ($nm) work in a way that they get applied if $nm captures exist. If there are less than nm but more than n captures available $n is replaced by the n'th capture and m is copied over verbatim. Change-Id: I8b5f576f2c42c8334859ab7854dcdf07104dd35b Fixes: QTBUG-73152 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp39
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp59
2 files changed, 84 insertions, 14 deletions
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index d0f6aff9d9..8186153ba4 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -722,41 +722,52 @@ static void appendReplacementString(QString *result, const QString &input, const
result->reserve(result->length() + replaceValue.length());
for (int i = 0; i < replaceValue.length(); ++i) {
if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) {
- ushort ch = replaceValue.at(++i).unicode();
+ ushort ch = replaceValue.at(i + 1).unicode();
uint substStart = JSC::Yarr::offsetNoMatch;
uint substEnd = JSC::Yarr::offsetNoMatch;
+ int skip = 0;
if (ch == '$') {
*result += QChar(ch);
+ ++i;
continue;
} else if (ch == '&') {
substStart = matchOffsets[0];
substEnd = matchOffsets[1];
+ skip = 1;
} else if (ch == '`') {
substStart = 0;
substEnd = matchOffsets[0];
+ skip = 1;
} else if (ch == '\'') {
substStart = matchOffsets[1];
substEnd = input.length();
- } else if (ch >= '1' && ch <= '9') {
+ skip = 1;
+ } else if (ch >= '0' && ch <= '9') {
uint capture = ch - '0';
- Q_ASSERT(capture > 0);
- if (capture < static_cast<uint>(captureCount)) {
+ skip = 1;
+ if (i < replaceValue.length() - 2) {
+ ch = replaceValue.at(i + 2).unicode();
+ if (ch >= '0' && ch <= '9') {
+ uint c = capture*10 + ch - '0';
+ if (c < static_cast<uint>(captureCount)) {
+ capture = c;
+ skip = 2;
+ }
+ }
+ }
+ if (capture > 0 && capture < static_cast<uint>(captureCount)) {
substStart = matchOffsets[capture * 2];
substEnd = matchOffsets[capture * 2 + 1];
- }
- } else if (ch == '0' && i < replaceValue.length() - 1) {
- int capture = (ch - '0') * 10;
- ch = replaceValue.at(++i).unicode();
- if (ch >= '0' && ch <= '9') {
- capture += ch - '0';
- if (capture > 0 && capture < captureCount) {
- substStart = matchOffsets[capture * 2];
- substEnd = matchOffsets[capture * 2 + 1];
- }
+ } else {
+ skip = 0;
}
}
+ i += skip;
if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
*result += input.midRef(substStart, substEnd - substStart);
+ else {
+ *result += replaceValue.at(i);
+ }
} else {
*result += replaceValue.at(i);
}
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index cf7b9c7224..42d8f37ca8 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -220,6 +220,8 @@ private slots:
void functionToString_data();
void functionToString();
+ void stringReplace();
+
void protoChanges_QTBUG68369();
void multilineStrings();
@@ -4367,6 +4369,63 @@ void tst_QJSEngine::functionToString()
QCOMPARE(evaluationResult.toString(), expectedString);
}
+void tst_QJSEngine::stringReplace()
+{
+ QJSEngine engine;
+
+ QJSValue val = engine.evaluate("'x'.replace('x', '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$1"));
+
+ val = engine.evaluate("'x'.replace('x', '$10')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$10"));
+
+ val = engine.evaluate("'x'.replace('x', '$01')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$01"));
+
+ val = engine.evaluate("'x'.replace('x', '$0')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$0"));
+
+ val = engine.evaluate("'x'.replace('x', '$00')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$00"));
+
+ val = engine.evaluate("'x'.replace(/(x)/, '$1')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("x"));
+
+ val = engine.evaluate("'x'.replace(/(x)/, '$01')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("x"));
+
+ val = engine.evaluate("'x'.replace(/(x)/, '$2')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$2"));
+
+ val = engine.evaluate("'x'.replace(/(x)/, '$02')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$02"));
+
+ val = engine.evaluate("'x'.replace(/(x)/, '$0')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$0"));
+
+ val = engine.evaluate("'x'.replace(/(x)/, '$00')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("$00"));
+
+ val = engine.evaluate("'x'.replace(/()()()()()()()()()(x)/, '$11')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("1"));
+
+ val = engine.evaluate("'x'.replace(/()()()()()()()()()(x)/, '$10')");
+ QVERIFY(val.isString());
+ QCOMPARE(val.toString(), QString("x"));
+}
+
void tst_QJSEngine::protoChanges_QTBUG68369()
{
QJSEngine engine;