From: Simon Hausmann Date: Mon, 21 Jan 2013 08:20:03 +0000 (+0100) Subject: Implement String.prototype.replace X-Git-Tag: upstream/5.2.1~669^2~659^2~459 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b3dc62609f27a6933eee8621f4eac57f9b90d100;p=platform%2Fupstream%2Fqtdeclarative.git Implement String.prototype.replace Change-Id: I60806b6563337c1c18a6b737e860deca4093c8ff Reviewed-by: Lars Knoll --- diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 933cb18..5024f86 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -793,11 +793,137 @@ Value StringPrototype::method_match(ExecutionContext *ctx) } +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < 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).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + Value StringPrototype::method_replace(ExecutionContext *ctx) { - // requires Regexp - ctx->throwUnimplemented(QStringLiteral("String.prototype.replace")); - return Value::undefinedValue(); + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.asRegExpObject(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); } Value StringPrototype::method_search(ExecutionContext *ctx) diff --git a/qv4regexp.cpp b/qv4regexp.cpp index 64d9fef..bcef815 100644 --- a/qv4regexp.cpp +++ b/qv4regexp.cpp @@ -46,7 +46,7 @@ namespace QQmlJS { namespace VM { -int RegExp::match(const QString &string, int start, uint *matchOffsets) +uint RegExp::match(const QString &string, int start, uint *matchOffsets) { if (!isValid()) return JSC::Yarr::offsetNoMatch; diff --git a/qv4regexp.h b/qv4regexp.h index 7fd8225..7eae033 100644 --- a/qv4regexp.h +++ b/qv4regexp.h @@ -69,7 +69,7 @@ public: bool isValid() const { return m_byteCode.get(); } - int match(const QString& string, int start, uint *matchOffsets); + uint match(const QString& string, int start, uint *matchOffsets); bool ignoreCase() const { return m_ignoreCase; } bool multiLine() const { return m_multiLine; } diff --git a/tests/TestExpectations b/tests/TestExpectations index c2e77f5..047d587 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -14,12 +14,6 @@ S10.2.3_A1.3_T2 failing 10.4.2-1-3 failing 10.4.2-1-4 failing 10.4.2-1-5 failing -10.4.3-1-100-s failing -10.4.3-1-100gs failing -10.4.3-1-101-s failing -10.4.3-1-101gs failing -10.4.3-1-102-s failing -10.4.3-1-102gs failing 10.4.3-1-104 failing 10.4.3-1-106 failing 10.4.3-1-17-s failing @@ -292,13 +286,6 @@ S15.1.2.2_A9.2 failing S15.1.2.2_A9.3 failing S15.1.2.2_A9.4 failing S15.1.2.2_A9.7 failing -S15.10.2.12_A6_T1 failing -S15.10.2.12_A1_T1 failing -S15.10.2.12_A2_T1 failing -S15.10.2.12_A3_T1 failing -S15.10.2.12_A4_T1 failing -S15.10.2.12_A5_T1 failing -S15.10.2.8_A3_T18 failing S15.10.6.2_A1_T2 failing S15.10.7_A2_T1 failing S15.11.4.2_A1 failing @@ -630,41 +617,7 @@ S15.4.4.7_A3 failing S15.4.4.7_A4_T2 failing S15.4.4.7_A4_T3 failing S15.5.4.10_A1_T10 failing -15.5.4.11-1 failing -S15.5.4.11_A12 failing -S15.5.4.11_A1_T1 failing S15.5.4.11_A1_T10 failing -S15.5.4.11_A1_T11 failing -S15.5.4.11_A1_T12 failing -S15.5.4.11_A1_T13 failing -S15.5.4.11_A1_T14 failing -S15.5.4.11_A1_T15 failing -S15.5.4.11_A1_T16 failing -S15.5.4.11_A1_T17 failing -S15.5.4.11_A1_T2 failing -S15.5.4.11_A1_T4 failing -S15.5.4.11_A1_T5 failing -S15.5.4.11_A1_T6 failing -S15.5.4.11_A1_T7 failing -S15.5.4.11_A1_T8 failing -S15.5.4.11_A1_T9 failing -S15.5.4.11_A2_T1 failing -S15.5.4.11_A2_T10 failing -S15.5.4.11_A2_T2 failing -S15.5.4.11_A2_T3 failing -S15.5.4.11_A2_T4 failing -S15.5.4.11_A2_T5 failing -S15.5.4.11_A2_T6 failing -S15.5.4.11_A2_T7 failing -S15.5.4.11_A2_T8 failing -S15.5.4.11_A2_T9 failing -S15.5.4.11_A3_T1 failing -S15.5.4.11_A3_T2 failing -S15.5.4.11_A3_T3 failing -S15.5.4.11_A4_T1 failing -S15.5.4.11_A4_T2 failing -S15.5.4.11_A4_T3 failing -S15.5.4.11_A4_T4 failing S15.5.4.11_A5_T1 failing S15.5.4.12_A1.1_T1 failing S15.5.4.12_A1_T1 failing @@ -1148,4 +1101,4 @@ S15.4.4.4_A1_T2 failing 15.4.4.21-8-b-iii-1-6 failing 15.12.3_4-1-1 -15.12.3_4-1-3 \ No newline at end of file +15.12.3_4-1-3