diff options
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject.cpp | 93 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject_p.h | 14 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 64 |
3 files changed, 169 insertions, 2 deletions
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 468fb34d76..0ebad2f781 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -238,6 +238,15 @@ RegExpCtor::RegExpCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("RegExp")) { setVTable(&static_vtbl); + clearLastMatch(); +} + +void RegExpCtor::clearLastMatch() +{ + lastMatch = Primitive::nullValue(); + lastInput = engine()->newIdentifier(QString()); + lastMatchStart = 0; + lastMatchEnd = 0; } ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData) @@ -300,6 +309,14 @@ ReturnedValue RegExpCtor::call(Managed *that, CallData *callData) return construct(that, callData); } +void RegExpCtor::markObjects(Managed *that, ExecutionEngine *e) +{ + RegExpCtor *This = static_cast<RegExpCtor*>(that); + This->lastMatch.mark(e); + This->lastInput.mark(e); + FunctionObject::markObjects(that, e); +} + void RegExpPrototype::init(ExecutionEngine *engine, ObjectRef ctor) { Scope scope(engine); @@ -307,6 +324,28 @@ void RegExpPrototype::init(ExecutionEngine *engine, ObjectRef ctor) ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(2)); + + // Properties deprecated in the spec but required by "the web" :( + ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, 0); + ctor->defineAccessorProperty(QStringLiteral("$&"), method_get_lastMatch_n<0>, 0); + ctor->defineAccessorProperty(QStringLiteral("$1"), method_get_lastMatch_n<1>, 0); + ctor->defineAccessorProperty(QStringLiteral("$2"), method_get_lastMatch_n<2>, 0); + ctor->defineAccessorProperty(QStringLiteral("$3"), method_get_lastMatch_n<3>, 0); + ctor->defineAccessorProperty(QStringLiteral("$4"), method_get_lastMatch_n<4>, 0); + ctor->defineAccessorProperty(QStringLiteral("$5"), method_get_lastMatch_n<5>, 0); + ctor->defineAccessorProperty(QStringLiteral("$6"), method_get_lastMatch_n<6>, 0); + ctor->defineAccessorProperty(QStringLiteral("$7"), method_get_lastMatch_n<7>, 0); + ctor->defineAccessorProperty(QStringLiteral("$8"), method_get_lastMatch_n<8>, 0); + ctor->defineAccessorProperty(QStringLiteral("$9"), method_get_lastMatch_n<9>, 0); + ctor->defineAccessorProperty(QStringLiteral("lastParen"), method_get_lastParen, 0); + ctor->defineAccessorProperty(QStringLiteral("$+"), method_get_lastParen, 0); + ctor->defineAccessorProperty(QStringLiteral("input"), method_get_input, 0); + ctor->defineAccessorProperty(QStringLiteral("$_"), method_get_input, 0); + ctor->defineAccessorProperty(QStringLiteral("leftContext"), method_get_leftContext, 0); + ctor->defineAccessorProperty(QStringLiteral("$`"), method_get_leftContext, 0); + ctor->defineAccessorProperty(QStringLiteral("rightContext"), method_get_rightContext, 0); + ctor->defineAccessorProperty(QStringLiteral("$'"), method_get_rightContext, 0); + defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(QStringLiteral("test"), method_test, 1); @@ -334,7 +373,11 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) } uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); - int result = r->value->match(s, offset, matchOffsets); + const int result = r->value->match(s, offset, matchOffsets); + + Scoped<RegExpCtor> regExpCtor(scope, ctx->engine->regExpCtor); + regExpCtor->clearLastMatch(); + if (result == -1) { r->lastIndexProperty(ctx)->value = Primitive::fromInt32(0); return Encode::null(); @@ -351,10 +394,14 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) array->arrayDataLen = i + 1; } array->setArrayLengthUnchecked(len); - array->memberData[Index_ArrayIndex].value = Primitive::fromInt32(result); array->memberData[Index_ArrayInput].value = arg.asReturnedValue(); + regExpCtor->lastMatch = array; + regExpCtor->lastInput = arg->stringValue(); + regExpCtor->lastMatchStart = matchOffsets[0]; + regExpCtor->lastMatchEnd = matchOffsets[1]; + if (r->global) r->lastIndexProperty(ctx)->value = Primitive::fromInt32(matchOffsets[1]); @@ -395,4 +442,46 @@ ReturnedValue RegExpPrototype::method_compile(CallContext *ctx) return Encode::undefined(); } +template <int index> +ReturnedValue RegExpPrototype::method_get_lastMatch_n(CallContext *ctx) +{ + Scope scope(ctx); + ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->engine->regExpCtor.objectValue())->lastMatch); + ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); + if (result->isUndefined()) + return ctx->engine->newString(QString())->asReturnedValue(); + return result.asReturnedValue(); +} + +ReturnedValue RegExpPrototype::method_get_lastParen(CallContext *ctx) +{ + Scope scope(ctx); + ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->engine->regExpCtor.objectValue())->lastMatch); + ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(lastMatch->arrayLength() - 1) : Encode::undefined()); + if (result->isUndefined()) + return ctx->engine->newString(QString())->asReturnedValue(); + return result.asReturnedValue(); +} + +ReturnedValue RegExpPrototype::method_get_input(CallContext *ctx) +{ + return static_cast<RegExpCtor*>(ctx->engine->regExpCtor.objectValue())->lastInput.asReturnedValue(); +} + +ReturnedValue RegExpPrototype::method_get_leftContext(CallContext *ctx) +{ + Scope scope(ctx); + Scoped<RegExpCtor> regExpCtor(scope, ctx->engine->regExpCtor); + QString lastInput = regExpCtor->lastInput->toQString(); + return ctx->engine->newString(lastInput.left(regExpCtor->lastMatchStart))->asReturnedValue(); +} + +ReturnedValue RegExpPrototype::method_get_rightContext(CallContext *ctx) +{ + Scope scope(ctx); + Scoped<RegExpCtor> regExpCtor(scope, ctx->engine->regExpCtor); + QString lastInput = regExpCtor->lastInput->toQString(); + return ctx->engine->newString(lastInput.mid(regExpCtor->lastMatchEnd))->asReturnedValue(); +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 0129f8d396..f112ad804d 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -106,8 +106,15 @@ struct RegExpCtor: FunctionObject Q_MANAGED RegExpCtor(ExecutionContext *scope); + SafeValue lastMatch; + SafeString lastInput; + int lastMatchStart; + int lastMatchEnd; + void clearLastMatch(); + static ReturnedValue construct(Managed *m, CallData *callData); static ReturnedValue call(Managed *that, CallData *callData); + static void markObjects(Managed *that, ExecutionEngine *e); }; struct RegExpPrototype: RegExpObject @@ -119,6 +126,13 @@ struct RegExpPrototype: RegExpObject static ReturnedValue method_test(CallContext *ctx); static ReturnedValue method_toString(CallContext *ctx); static ReturnedValue method_compile(CallContext *ctx); + + template <int index> + static ReturnedValue method_get_lastMatch_n(CallContext *ctx); + static ReturnedValue method_get_lastParen(CallContext *ctx); + static ReturnedValue method_get_input(CallContext *ctx); + static ReturnedValue method_get_leftContext(CallContext *ctx); + static ReturnedValue method_get_rightContext(CallContext *ctx); }; } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index a1662b495c..ba99b34935 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -148,6 +148,8 @@ private slots: void functionDeclarationsInConditionals(); void arrayPop_QTBUG_35979(); + + void regexpLastMatch(); }; tst_QJSEngine::tst_QJSEngine() @@ -2705,6 +2707,68 @@ void tst_QJSEngine::arrayPop_QTBUG_35979() QCOMPARE(result.toString(), QString("1,3")); } +void tst_QJSEngine::regexpLastMatch() +{ + QJSEngine eng; + + QCOMPARE(eng.evaluate("RegExp.input").toString(), QString()); + + QJSValue hasProperty; + + for (int i = 1; i < 9; ++i) { + hasProperty = eng.evaluate("RegExp.hasOwnProperty(\"$" + QString::number(i) + "\")"); + QVERIFY(hasProperty.isBool()); + QVERIFY(hasProperty.toBool()); + } + + hasProperty = eng.evaluate("RegExp.hasOwnProperty(\"$0\")"); + QVERIFY(hasProperty.isBool()); + QVERIFY(!hasProperty.toBool()); + + hasProperty = eng.evaluate("RegExp.hasOwnProperty(\"$10\")"); + QVERIFY(!hasProperty.toBool()); + + hasProperty = eng.evaluate("RegExp.hasOwnProperty(\"lastMatch\")"); + QVERIFY(hasProperty.toBool()); + hasProperty = eng.evaluate("RegExp.hasOwnProperty(\"$&\")"); + QVERIFY(hasProperty.toBool()); + + QJSValue result = eng.evaluate("" + "var re = /h(el)l(o)/\n" + "var text = \"blah hello world\"\n" + "text.match(re)\n"); + QVERIFY(!result.isError()); + QJSValue match = eng.evaluate("RegExp.$1"); + QCOMPARE(match.toString(), QString("el")); + match = eng.evaluate("RegExp.$2"); + QCOMPARE(match.toString(), QString("o")); + for (int i = 3; i <= 9; ++i) { + match = eng.evaluate("RegExp.$" + QString::number(i)); + QVERIFY(match.isString()); + QCOMPARE(match.toString(), QString()); + } + QCOMPARE(eng.evaluate("RegExp.input").toString(), QString("blah hello world")); + QCOMPARE(eng.evaluate("RegExp.lastParen").toString(), QString("o")); + QCOMPARE(eng.evaluate("RegExp.leftContext").toString(), QString("blah ")); + QCOMPARE(eng.evaluate("RegExp.rightContext").toString(), QString(" world")); + + QCOMPARE(eng.evaluate("RegExp.lastMatch").toString(), QString("hello")); + + result = eng.evaluate("" + "var re = /h(ello)/\n" + "var text = \"hello\"\n" + "text.match(re)\n"); + QVERIFY(!result.isError()); + match = eng.evaluate("RegExp.$1"); + QCOMPARE(match.toString(), QString("ello")); + for (int i = 2; i <= 9; ++i) { + match = eng.evaluate("RegExp.$" + QString::number(i)); + QVERIFY(match.isString()); + QCOMPARE(match.toString(), QString()); + } + +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" |