aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2014-01-20 14:32:26 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-20 16:10:33 +0100
commit19025ab3422658ab27415cee99336d88a4ae19fa (patch)
tree48be7ca9b74b73ce5f68b265ac4bdb485dc827c3
parent46ffd0e470b66e8cc501d5caf4ba92022d1ee474 (diff)
Add support for deprecated RegExp (constructor) properties
These were apparently part of ancient EcmaScript specs, aren't even listed anymore in any recent spec (not even as deprecated), but apparently they are part of what the web supports as well as previous versions of Qml. So this patch implements them. Task-number: QTBUG-36244 Change-Id: I1b9ea7ea09fceb6a486f615837a71e41aae12de4 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-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"