Support QRegularExpression on the same level as QRegExp

QRegularExpression is the recommended way to do regular expressions
nowadays. Support assignment of JavaScript regular expressions to
QRegularExpression properties of QObjects and the other way around.
QJSValue::toVariant() will create a QRegularExpression from a JavaScript
RegExp by default now.

[ChangeLog][QtQml][Important Behavior Changes] QRegularExpression is now
supported the same way QRegExp is in QML. QJSValue::toVariant() creates
a QRegularExpression variant rather than a QRegExp one from a JavaScript
regular expression now.

Fixes: QTBUG-73429
Change-Id: I301a02771cd17903406c2bc5c7aaeca6cce629f0
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Ulf Hermann 2019-02-13 15:54:14 +01:00
parent 80920a3012
commit d2fd8010d3
15 changed files with 282 additions and 29 deletions

View File

@ -558,6 +558,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
}
break;
case QVariant::RegExp:
case QVariant::RegularExpression:
return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
default: {
// generate single literal value assignment to a list property if required

View File

@ -51,6 +51,9 @@
#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
#if QT_CONFIG(regularexpression)
#include <QRegularExpression>
#endif
#ifndef V4_BOOTSTRAP
@ -854,6 +857,13 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
return memoryManager->allocate<RegExpObject>(re);
}
#if QT_CONFIG(regularexpression)
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re)
{
return memoryManager->allocate<RegExpObject>(re);
}
#endif
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
{
return ErrorObject::create<ErrorObject>(this, value, errorCtor());
@ -1365,8 +1375,13 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
QV4::ScopedObject o(scope, value);
Q_ASSERT(o);
if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>())
if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) {
#if QT_CONFIG(regularexpression)
if (typeHint != QMetaType::QRegExp)
return re->toQRegularExpression();
#endif
return re->toQRegExp();
}
if (createJSValueForObjects)
return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue()));
@ -1455,8 +1470,6 @@ static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QV
return o.asReturnedValue();
}
Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
{
int type = variant.userType();
@ -1506,6 +1519,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr)));
case QMetaType::QRegExp:
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr)));
#endif
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
#if QT_CONFIG(qml_sequence_object)
@ -1713,6 +1730,10 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(data))));
case QMetaType::QRegExp:
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(data)));
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(data)));
#endif
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data));
case QMetaType::QVariant:
@ -1955,6 +1976,13 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
*reinterpret_cast<QRegExp *>(data) = r->toQRegExp();
return true;
} break;
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) {
*reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression();
return true;
} break;
#endif
case QMetaType::QObjectStar: {
const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>();
if (qobjectWrapper || value->isNull()) {

View File

@ -520,6 +520,9 @@ public:
Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags);
Heap::RegExpObject *newRegExpObject(RegExp *re);
Heap::RegExpObject *newRegExpObject(const QRegExp &re);
#if QT_CONFIG(regularexpression)
Heap::RegExpObject *newRegExpObject(const QRegularExpression &re);
#endif
Heap::Object *newErrorObject(const Value &value);
Heap::Object *newErrorObject(const QString &message);

View File

@ -1317,6 +1317,9 @@ static int MatchScore(const QV4::Value &actual, int conversionType)
} else if (actual.as<QV4::RegExpObject>()) {
switch (conversionType) {
case QMetaType::QRegExp:
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
#endif
return 0;
default:
return 10;

View File

@ -50,6 +50,9 @@
#include <QtCore/QDebug>
#include <QtCore/qregexp.h>
#if QT_CONFIG(regularexpression)
#include <QtCore/qregularexpression.h>
#endif
#include <cassert>
#include <typeinfo>
#include <iostream>
@ -134,6 +137,25 @@ void Heap::RegExpObject::init(const QRegExp &re)
o->initProperties();
}
#if QT_CONFIG(regularexpression)
// Converts a QRegularExpression to a JS RegExp.
// The conversion is not 100% exact since ECMA regexp and QRegularExpression
// have different semantics/flags, but we try to do our best.
void Heap::RegExpObject::init(const QRegularExpression &re)
{
Object::init();
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
const uint flags = (re.patternOptions() & QRegularExpression::CaseInsensitiveOption)
? CompiledData::RegExp::RegExp_IgnoreCase
: CompiledData::RegExp::RegExp_NoFlags;
o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, re.pattern(), flags));
o->initProperties();
}
#endif
void RegExpObject::initProperties()
{
setProperty(Index_LastIndex, Value::fromInt32(0));
@ -150,6 +172,20 @@ QRegExp RegExpObject::toQRegExp() const
return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2);
}
#if QT_CONFIG(regularexpression)
// Converts a JS RegExp to a QRegularExpression.
// The conversion is not 100% exact since ECMA regexp and QRegularExpression
// have different semantics/flags, but we try to do our best.
QRegularExpression RegExpObject::toQRegularExpression() const
{
QRegularExpression::PatternOptions caseSensitivity
= (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase)
? QRegularExpression::CaseInsensitiveOption
: QRegularExpression::NoPatternOption;
return QRegularExpression(*value()->pattern, caseSensitivity);
}
#endif
QString RegExpObject::toString() const
{
QString p = *value()->pattern;

View File

@ -81,6 +81,9 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) {
void init();
void init(QV4::RegExp *value);
void init(const QRegExp &re);
#if QT_CONFIG(regularexpression)
void init(const QRegularExpression &re);
#endif
};
#define RegExpCtorMembers(class, Member) \
@ -138,6 +141,9 @@ struct RegExpObject: Object {
}
QRegExp toQRegExp() const;
#if QT_CONFIG(regularexpression)
QRegularExpression toQRegularExpression() const;
#endif
QString toString() const;
QString source() const;

View File

@ -62,7 +62,9 @@
#include <QtCore/qdebug.h>
#include <QtCore/QUrl>
#include <QtCore/QDir>
#if QT_CONFIG(regularexpression)
#include <QtCore/qregularexpression.h>
#endif
#include <QtQuick/qquickwindow.h>
#include <QtGui/qvector3d.h>
#include <QtGui/qimagewriter.h>
@ -640,12 +642,9 @@ void QuickTestResult::warn(const QString &message, const QUrl &location, int lin
void QuickTestResult::ignoreWarning(const QJSValue &message)
{
if (message.isRegExp()) {
// ### we should probably handle QRegularExpression conversion engine-side
QRegExp re = message.toVariant().toRegExp();
QRegularExpression::PatternOptions opts = re.caseSensitivity() ==
Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption;
QRegularExpression re2(re.pattern(), opts);
QTestLog::ignoreMessage(QtWarningMsg, re2);
#if QT_CONFIG(regularexpression)
QTestLog::ignoreMessage(QtWarningMsg, message.toVariant().toRegularExpression());
#endif
} else {
QTestLog::ignoreMessage(QtWarningMsg, message.toString().toLatin1());
}

View File

@ -94,6 +94,7 @@ private slots:
void valueConversion_basic2();
void valueConversion_dateTime();
void valueConversion_regExp();
void valueConversion_RegularExpression();
void castWithMultipleInheritance();
void collectGarbage();
void gcWithNestedDataStructure();
@ -135,6 +136,8 @@ private slots:
void qRegExpInport_data();
void qRegExpInport();
void qRegularExpressionImport_data();
void qRegularExpressionImport();
void dateRoundtripJSQtJS();
void dateRoundtripQtJSQt();
void dateConversionJSQt();
@ -520,22 +523,27 @@ void tst_QJSEngine::newVariant_valueOfEnum()
void tst_QJSEngine::newRegExp()
{
QJSEngine eng;
QJSValue rexp = eng.toScriptValue(QRegExp("foo"));
QVERIFY(!rexp.isUndefined());
QCOMPARE(rexp.isRegExp(), true);
QCOMPARE(rexp.isObject(), true);
QCOMPARE(rexp.isCallable(), false);
// prototype should be RegExp.prototype
QVERIFY(!rexp.prototype().isUndefined());
QCOMPARE(rexp.prototype().isObject(), true);
// Get [[Class]] internal property of RegExp Prototype Object.
// See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
// See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
QCOMPARE(r.toString(), QString::fromLatin1("[object Object]"));
QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
QJSValue rexps[] = {
eng.toScriptValue(QRegularExpression("foo")),
eng.toScriptValue(QRegExp("foo"))
};
for (const auto &rexp : rexps) {
QVERIFY(!rexp.isUndefined());
QCOMPARE(rexp.isRegExp(), true);
QCOMPARE(rexp.isObject(), true);
QCOMPARE(rexp.isCallable(), false);
// prototype should be RegExp.prototype
QVERIFY(!rexp.prototype().isUndefined());
QCOMPARE(rexp.prototype().isObject(), true);
// Get [[Class]] internal property of RegExp Prototype Object.
// See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods".
// See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object".
QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)");
QCOMPARE(r.toString(), QString::fromLatin1("[object Object]"));
QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true);
QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern());
}
}
void tst_QJSEngine::jsRegExp()
@ -1601,6 +1609,28 @@ void tst_QJSEngine::valueConversion_regExp()
}
}
void tst_QJSEngine::valueConversion_RegularExpression()
{
QJSEngine eng;
{
QRegularExpression in = QRegularExpression("foo");
QJSValue val = eng.toScriptValue(in);
QVERIFY(val.isRegExp());
QRegularExpression out = qjsvalue_cast<QRegularExpression>(val);
QCOMPARE(out.pattern(), in.pattern());
QCOMPARE(out.patternOptions(), in.patternOptions());
}
{
QRegularExpression in = QRegularExpression("foo",
QRegularExpression::CaseInsensitiveOption);
QJSValue val = eng.toScriptValue(in);
QVERIFY(val.isRegExp());
QCOMPARE(qjsvalue_cast<QRegularExpression>(val), in);
QRegularExpression out = qjsvalue_cast<QRegularExpression>(val);
QCOMPARE(out.patternOptions(), in.patternOptions());
}
}
Q_DECLARE_METATYPE(QGradient)
Q_DECLARE_METATYPE(QGradient*)
Q_DECLARE_METATYPE(QLinearGradient)
@ -2950,6 +2980,8 @@ void tst_QJSEngine::reentrancy_objectCreation()
QJSValue r2 = eng2.evaluate("new RegExp('foo', 'gim')");
QCOMPARE(qjsvalue_cast<QRegExp>(r1), qjsvalue_cast<QRegExp>(r2));
QCOMPARE(qjsvalue_cast<QRegExp>(r2), qjsvalue_cast<QRegExp>(r1));
QCOMPARE(qjsvalue_cast<QRegularExpression>(r1), qjsvalue_cast<QRegularExpression>(r2));
QCOMPARE(qjsvalue_cast<QRegularExpression>(r2), qjsvalue_cast<QRegularExpression>(r1));
}
{
QJSValue o1 = eng1.newQObject(temp);
@ -3145,6 +3177,56 @@ void tst_QJSEngine::qRegExpInport()
}
}
void tst_QJSEngine::qRegularExpressionImport_data()
{
QTest::addColumn<QRegularExpression>("rx");
QTest::addColumn<QString>("string");
QTest::addColumn<QString>("matched");
QTest::newRow("normal") << QRegularExpression("(test|foo)") << "test _ foo _ test _ Foo";
QTest::newRow("normal2") << QRegularExpression("(Test|Foo)") << "test _ foo _ test _ Foo";
QTest::newRow("case insensitive") << QRegularExpression("(test|foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo";
QTest::newRow("case insensitive2") << QRegularExpression("(Test|Foo)", QRegularExpression::CaseInsensitiveOption) << "test _ foo _ test _ Foo";
QTest::newRow("b(a*)(b*)") << QRegularExpression("b(a*)(b*)", QRegularExpression::CaseInsensitiveOption) << "aaabbBbaAabaAaababaaabbaaab";
QTest::newRow("greedy") << QRegularExpression("a*(a*)", QRegularExpression::CaseInsensitiveOption) << "aaaabaaba";
QTest::newRow("wildcard") << QRegularExpression(".*\\.txt") << "file.txt";
QTest::newRow("wildcard 2") << QRegularExpression("a.b\\.txt") << "ab.txt abb.rtc acb.txt";
QTest::newRow("slash") << QRegularExpression("g/.*/s", QRegularExpression::CaseInsensitiveOption) << "string/string/string";
QTest::newRow("slash2") << QRegularExpression("g / .* / s", QRegularExpression::CaseInsensitiveOption) << "string / string / string";
QTest::newRow("fixed") << QRegularExpression("a\\*aa\\.a\\(ba\\)\\*a\\\\ba", QRegularExpression::CaseInsensitiveOption) << "aa*aa.a(ba)*a\\ba";
QTest::newRow("fixed insensitive") << QRegularExpression("A\\*A", QRegularExpression::CaseInsensitiveOption) << "a*A A*a A*A a*a";
QTest::newRow("fixed sensitive") << QRegularExpression("A\\*A") << "a*A A*a A*A a*a";
QTest::newRow("html") << QRegularExpression("<b>(.*)</b>") << "<b>bold</b><i>italic</i><b>bold</b>";
QTest::newRow("html minimal") << QRegularExpression("^<b>(.*)</b>$") << "<b>bold</b><i>italic</i><b>bold</b>";
QTest::newRow("aaa") << QRegularExpression("a{2,5}") << "aAaAaaaaaAa";
QTest::newRow("aaa minimal") << QRegularExpression("^a{2,5}$") << "aAaAaaaaaAa";
QTest::newRow("minimal") << QRegularExpression("^.*\\} [*8]$") << "}?} ?} *";
QTest::newRow(".? minimal") << QRegularExpression("^.?$") << ".?";
QTest::newRow(".+ minimal") << QRegularExpression("^.+$") << ".+";
QTest::newRow("[.?] minimal") << QRegularExpression("^[.?]$") << ".?";
QTest::newRow("[.+] minimal") << QRegularExpression("^[.+]$") << ".+";
}
void tst_QJSEngine::qRegularExpressionImport()
{
QFETCH(QRegularExpression, rx);
QFETCH(QString, string);
QJSEngine eng;
QJSValue rexp;
rexp = eng.toScriptValue(rx);
QCOMPARE(rexp.isRegExp(), true);
QCOMPARE(rexp.isCallable(), false);
QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })");
QJSValue result = func.call(QJSValueList() << string << rexp);
const QRegularExpressionMatch match = rx.match(string);
for (int i = 0; i <= match.lastCapturedIndex(); i++)
QCOMPARE(result.property(i).toString(), match.captured(i));
}
// QScriptValue::toDateTime() returns a local time, whereas JS dates
// are always stored as UTC. Qt Script must respect the current time
// zone, and correctly adjust for daylight saving time that may be in

View File

@ -1027,6 +1027,20 @@ void tst_QJSValue::toVariant()
QJSValue rxObject = eng.toScriptValue(rx);
QVERIFY(rxObject.isRegExp());
QVariant var = rxObject.toVariant();
// We can't roundtrip a QRegExp this way, as toVariant() has no information on whether we
// want QRegExp or QRegularExpression. It will always create a QRegularExpression.
QCOMPARE(var.type(), QMetaType::QRegularExpression);
QRegularExpression result = var.toRegularExpression();
QCOMPARE(result.pattern(), rx.pattern());
QCOMPARE(result.patternOptions() & QRegularExpression::CaseInsensitiveOption, 0);
}
{
QRegularExpression rx = QRegularExpression("[0-9a-z]+");
QJSValue rxObject = eng.toScriptValue(rx);
QVERIFY(rxObject.isRegExp());
QVariant var = rxObject.toVariant();
QCOMPARE(var, QVariant(rx));
}
@ -1194,6 +1208,32 @@ void tst_QJSValue::toRegExp()
QVERIFY(qjsvalue_cast<QRegExp>(eng.toScriptValue(QVariant())).isEmpty());
}
void tst_QJSValue::toRegularExpression()
{
QJSEngine eng;
{
QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/foo/"));
QVERIFY(rx.isValid());
QCOMPARE(rx.pattern(), QString::fromLatin1("foo"));
QVERIFY(!(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption));
}
{
QRegularExpression rx = qjsvalue_cast<QRegularExpression>(eng.evaluate("/bar/gi"));
QVERIFY(rx.isValid());
QCOMPARE(rx.pattern(), QString::fromLatin1("bar"));
QVERIFY(rx.patternOptions() & QRegularExpression::CaseInsensitiveOption);
}
QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("[]")).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("{}")).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(eng.globalObject()).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue()).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(123)).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(QJSValue(false)).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(eng.evaluate("null")).pattern().isEmpty());
QVERIFY(qjsvalue_cast<QRegularExpression>(eng.toScriptValue(QVariant())).pattern().isEmpty());
}
void tst_QJSValue::isArray_data()
{
newEngine();

View File

@ -76,6 +76,7 @@ private slots:
void toQObject();
void toDateTime();
void toRegExp();
void toRegularExpression();
void isArray_data();
void isArray();
void isDate();

View File

@ -0,0 +1,7 @@
import Qt.test 1.0
MyQmlObject{
id: obj
objectName: "obj"
regularExpression: "[a-zA-z]"
}

View File

@ -0,0 +1,7 @@
import Qt.test 1.0
MyQmlObject{
id: obj
objectName: "obj"
regularExpression: /[a-zA-z]/
}

View File

@ -33,6 +33,7 @@
#include <QtQml/qqmlexpression.h>
#include <QtCore/qpoint.h>
#include <QtCore/qsize.h>
#include <QtCore/qregularexpression.h>
#include <QtQml/qqmllist.h>
#include <QtCore/qrect.h>
#include <QtGui/qmatrix.h>
@ -101,6 +102,7 @@ class MyQmlObject : public QObject
Q_PROPERTY(QQmlListProperty<QObject> objectListProperty READ objectListProperty CONSTANT)
Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp)
Q_PROPERTY(QRegularExpression regularExpression READ regularExpression WRITE setRegularExpression)
Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false)
Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged)
Q_PROPERTY(QJSValue qjsvalue READ qjsvalue WRITE setQJSValue NOTIFY qjsvalueChanged)
@ -170,6 +172,12 @@ public:
QRegExp regExp() { return m_regExp; }
void setRegExp(const QRegExp &regExp) { m_regExp = regExp; }
QRegularExpression regularExpression() { return m_regularExpression; }
void setRegularExpression(const QRegularExpression &regularExpression)
{
m_regularExpression = regularExpression;
}
int console() const { return 11; }
int nonscriptable() const { return 0; }
@ -270,6 +278,7 @@ private:
int m_value;
int m_resetProperty;
QRegExp m_regExp;
QRegularExpression m_regularExpression;
QVariant m_variant;
QJSValue m_qjsvalue;
int m_intProperty;

View File

@ -2418,6 +2418,13 @@ void tst_qqmlecmascript::regExpBug()
delete object;
}
{
QQmlComponent component(&engine, testFileUrl("regularExpression.qml"));
QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(component.create()));
QVERIFY(!object.isNull());
QCOMPARE(object->regularExpression().pattern(), QLatin1String("[a-zA-z]"));
}
//QTBUG-23068
{
QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString());
@ -2427,6 +2434,18 @@ void tst_qqmlecmascript::regExpBug()
QVERIFY(!object);
QCOMPARE(component.errorString(), err);
}
{
const QString err = QString::fromLatin1("%1:6 Invalid property assignment: "
"regular expression expected; "
"use /pattern/ syntax\n")
.arg(testFileUrl("regularExpression.2.qml").toString());
QQmlComponent component(&engine, testFileUrl("regularExpression.2.qml"));
QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
QVERIFY(!object);
QCOMPARE(component.errorString(), err);
}
}
static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source)

View File

@ -30,6 +30,7 @@
#include <QtCore/qtimer.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qregularexpression.h>
#include <QtQml/qjsengine.h>
#include <QtQml/qqmlcomponent.h>
@ -118,7 +119,18 @@ void tst_QQuickWorkerScript::messaging()
QVariant response = mo->property(mo->indexOfProperty("response")).read(worker).value<QVariant>();
if (response.userType() == qMetaTypeId<QJSValue>())
response = response.value<QJSValue>().toVariant();
QCOMPARE(response, value);
if (value.type() == QMetaType::QRegExp && response.type() == QMetaType::QRegularExpression) {
// toVariant() doesn't know if we want QRegExp or QRegularExpression. It always creates
// a QRegularExpression from a JavaScript regular expression.
const QRegularExpression responseRegExp = response.toRegularExpression();
const QRegExp valueRegExp = value.toRegExp();
QCOMPARE(responseRegExp.pattern(), valueRegExp.pattern());
QCOMPARE(bool(responseRegExp.patternOptions() & QRegularExpression::CaseInsensitiveOption),
bool(valueRegExp.caseSensitivity() == Qt::CaseInsensitive));
} else {
QCOMPARE(response, value);
}
qApp->processEvents();
delete worker;
@ -135,10 +147,10 @@ void tst_QQuickWorkerScript::messaging_data()
QTest::newRow("string") << qVariantFromValue(QString("More cheeeese, Gromit!"));
QTest::newRow("variant list") << qVariantFromValue((QVariantList() << "a" << "b" << "c"));
QTest::newRow("date time") << qVariantFromValue(QDateTime::currentDateTime());
#ifndef QT_NO_REGEXP
// Qt Script's QScriptValue -> QRegExp uses RegExp2 pattern syntax
QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive, QRegExp::RegExp2));
#endif
QTest::newRow("regexp") << qVariantFromValue(QRegExp("^\\d\\d?$", Qt::CaseInsensitive,
QRegExp::RegExp2));
QTest::newRow("regularexpression") << qVariantFromValue(QRegularExpression(
"^\\d\\d?$", QRegularExpression::CaseInsensitiveOption));
}
void tst_QQuickWorkerScript::messaging_sendQObjectList()