diff options
author | Lars Knoll <lars.knoll@qt.io> | 2019-01-22 13:38:44 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2019-01-25 21:55:55 +0000 |
commit | cf04d5ee18344d45da538810654690bd3936f46a (patch) | |
tree | f3d2f81f54865d2ce713407490d48bd0549c4e98 | |
parent | f4ab0ab62496e63db72592845b285bab3d8388b9 (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.cpp | 39 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 59 |
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; |