aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp93
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h14
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp64
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"