diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index daa6765dcc..26988a4420 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -699,6 +699,12 @@ QT_BEGIN_NAMESPACE \c{\w} to match any character with either the Unicode L (letter) or N (digit) property, plus underscore, and so on. This option corresponds to the \c{/u} modifier in Perl regular expressions. + + \value OptimizeOnFirstUsageOption + The regular expression will be optimized (and possibly + JIT-compiled) on its first usage, instead of after a certain (undefined) + number of usages. See also \l{QRegularExpression::}{optimize()}. + This enum value has been introduced in Qt 5.4. */ /*! @@ -802,7 +808,13 @@ struct QRegularExpressionPrivate : QSharedData void cleanCompiledPattern(); void compilePattern(); void getPatternInfo(); - void optimizePattern(); + + enum OptimizePatternOption { + LazyOptimizeOption, + ImmediateOptimizeOption + }; + + void optimizePattern(OptimizePatternOption option); QRegularExpressionMatchPrivate *doMatch(const QString &subject, int offset, @@ -1107,13 +1119,16 @@ static bool isJitEnabled() (localStudyData) before using storeRelease on studyData. In doMatch there's the corresponding loadAcquire. */ -void QRegularExpressionPrivate::optimizePattern() +void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option) { Q_ASSERT(compiledPattern); QMutexLocker lock(&mutex); - if (studyData.load() || (++usedCount != qt_qregularexpression_optimize_after_use_count)) + if (studyData.load()) // already optimized + return; + + if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count)) return; static const bool enableJit = isJitEnabled(); @@ -1233,8 +1248,13 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString matchType, matchOptions, capturingCount + 1); + const OptimizePatternOption optimizePatternOption = + (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption) + ? ImmediateOptimizeOption + : LazyOptimizeOption; + // this is mutex protected - const_cast(this)->optimizePattern(); + const_cast(this)->optimizePattern(optimizePatternOption); // work with a local copy of the study data, as we are running pcre_exec // potentially more than once, and we don't want to run call it @@ -1666,6 +1686,27 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &s return QRegularExpressionMatchIterator(*priv); } +/*! + \since 5.4 + + Forces an immediate optimization of the pattern, including + JIT-compiling it (if the JIT compiler is enabled). + + Patterns are normally optimized only after a certain number of usages. + If you can predict that this QRegularExpression object is going to be + used for several matches, it may be convenient to optimize it in + advance by calling this function. + + \sa QRegularExpression::OptimizeOnFirstUsageOption +*/ +void QRegularExpression::optimize() +{ + if (!isValid()) // will compile the pattern + return; + + d->optimizePattern(QRegularExpressionPrivate::ImmediateOptimizeOption); +} + /*! Returns \c true if the regular expression is equal to \a re, or false otherwise. Two QRegularExpression objects are equal if they have @@ -2327,6 +2368,8 @@ QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOption flags.append("DontCaptureOption|"); if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) flags.append("UseUnicodePropertiesOption|"); + if (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption) + flags.append("OptimizeOnFirstUsageOption|"); flags.chop(1); } diff --git a/src/corelib/tools/qregularexpression.h b/src/corelib/tools/qregularexpression.h index 5059ea6431..6154ea7c51 100644 --- a/src/corelib/tools/qregularexpression.h +++ b/src/corelib/tools/qregularexpression.h @@ -69,7 +69,8 @@ public: ExtendedPatternSyntaxOption = 0x0008, InvertedGreedinessOption = 0x0010, DontCaptureOption = 0x0020, - UseUnicodePropertiesOption = 0x0040 + UseUnicodePropertiesOption = 0x0040, + OptimizeOnFirstUsageOption = 0x0080 }; Q_DECLARE_FLAGS(PatternOptions, PatternOption) @@ -122,6 +123,8 @@ public: MatchType matchType = NormalMatch, MatchOptions matchOptions = NoMatchOption) const; + void optimize(); + static QString escape(const QString &str); bool operator==(const QRegularExpression &re) const; diff --git a/tests/auto/corelib/tools/qregularexpression/.gitignore b/tests/auto/corelib/tools/qregularexpression/.gitignore index c9249e090e..4650b4454e 100644 --- a/tests/auto/corelib/tools/qregularexpression/.gitignore +++ b/tests/auto/corelib/tools/qregularexpression/.gitignore @@ -1,2 +1,3 @@ tst_qregularexpression_alwaysoptimize tst_qregularexpression_defaultoptimize +tst_qregularexpression_forceoptimize diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro b/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro new file mode 100644 index 0000000000..d34bc9b93d --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro @@ -0,0 +1,8 @@ +CONFIG += testcase parallel_test +TARGET = tst_qregularexpression_forceoptimize +QT = core testlib +HEADERS = ../tst_qregularexpression.h +SOURCES = \ + tst_qregularexpression_forceoptimize.cpp \ + ../tst_qregularexpression.cpp +DEFINES += forceOptimize=true diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp new file mode 100644 index 0000000000..6244aacedf --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "../tst_qregularexpression.h" + +class tst_QRegularExpression_ForceOptimize : public tst_QRegularExpression +{ + Q_OBJECT +}; + +QTEST_APPLESS_MAIN(tst_QRegularExpression_ForceOptimize) + +#include "tst_qregularexpression_forceoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro index 0cae10112f..c030f04a27 100644 --- a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro +++ b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = defaultoptimize +SUBDIRS = defaultoptimize forceoptimize contains(QT_CONFIG,private_tests):SUBDIRS += alwaysoptimize diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp index 909725f4b8..5fad1bb738 100644 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp @@ -48,6 +48,10 @@ #include "tst_qregularexpression.h" +#ifndef forceOptimize +#define forceOptimize false +#endif + struct Match { Match() @@ -329,22 +333,30 @@ void tst_QRegularExpression::gettersSetters() { QRegularExpression re; re.setPattern(pattern); + if (forceOptimize) + re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); } { QRegularExpression re; re.setPatternOptions(patternOptions); + if (forceOptimize) + re.optimize(); QCOMPARE(re.pattern(), QString()); QCOMPARE(re.patternOptions(), patternOptions); } { QRegularExpression re(pattern); + if (forceOptimize) + re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); } { QRegularExpression re(pattern, patternOptions); + if (forceOptimize) + re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), patternOptions); } @@ -385,6 +397,8 @@ void tst_QRegularExpression::escape() QFETCH(QString, escaped); QCOMPARE(QRegularExpression::escape(string), escaped); QRegularExpression re(escaped); + if (forceOptimize) + re.optimize(); QCOMPARE(re.isValid(), true); } @@ -415,6 +429,8 @@ void tst_QRegularExpression::validity() QFETCH(QString, pattern); QFETCH(bool, validity); QRegularExpression re(pattern); + if (forceOptimize) + re.optimize(); QCOMPARE(re.isValid(), validity); if (!validity) QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); @@ -501,6 +517,9 @@ void tst_QRegularExpression::patternOptions() QFETCH(QString, subject); QFETCH(Match, match); + if (forceOptimize) + regexp.optimize(); + QRegularExpressionMatch m = regexp.match(subject); consistencyCheck(m); QVERIFY(m == match); @@ -717,6 +736,9 @@ void tst_QRegularExpression::normalMatch() QFETCH(QRegularExpression::MatchOptions, matchOptions); QFETCH(Match, match); + if (forceOptimize) + regexp.optimize(); + { QRegularExpressionMatch m = regexp.match(subject, offset, QRegularExpression::NormalMatch, matchOptions); consistencyCheck(m); @@ -995,6 +1017,9 @@ void tst_QRegularExpression::partialMatch() QFETCH(QRegularExpression::MatchOptions, matchOptions); QFETCH(Match, match); + if (forceOptimize) + regexp.optimize(); + { QRegularExpressionMatch m = regexp.match(subject, offset, matchType, matchOptions); consistencyCheck(m); @@ -1286,6 +1311,10 @@ void tst_QRegularExpression::globalMatch() QFETCH(QRegularExpression::MatchType, matchType); QFETCH(QRegularExpression::MatchOptions, matchOptions); QFETCH(QList, matchList); + + if (forceOptimize) + regexp.optimize(); + { QRegularExpressionMatchIterator iterator = regexp.globalMatch(subject, offset, matchType, matchOptions); consistencyCheck(iterator); @@ -1320,6 +1349,10 @@ void tst_QRegularExpression::serialize() QFETCH(QString, pattern); QFETCH(QRegularExpression::PatternOptions, patternOptions); QRegularExpression outRe(pattern, patternOptions); + + if (forceOptimize) + outRe.optimize(); + QByteArray buffer; { QDataStream out(&buffer, QIODevice::WriteOnly); @@ -1376,16 +1409,34 @@ void tst_QRegularExpression::operatoreq() { QRegularExpression re1(pattern); QRegularExpression re2(pattern); + + if (forceOptimize) + re1.optimize(); + if (forceOptimize) + re2.optimize(); + verifyEquality(re1, re2); } { QRegularExpression re1(QString(), patternOptions); QRegularExpression re2(QString(), patternOptions); + + if (forceOptimize) + re1.optimize(); + if (forceOptimize) + re2.optimize(); + verifyEquality(re1, re2); } { QRegularExpression re1(pattern, patternOptions); QRegularExpression re2(pattern, patternOptions); + + if (forceOptimize) + re1.optimize(); + if (forceOptimize) + re2.optimize(); + verifyEquality(re1, re2); } } @@ -1414,6 +1465,10 @@ void tst_QRegularExpression::captureCount() { QFETCH(QString, pattern); QRegularExpression re(pattern); + + if (forceOptimize) + re.optimize(); + QTEST(re.captureCount(), "captureCount"); if (!re.isValid()) QCOMPARE(re.captureCount(), -1); @@ -1480,7 +1535,11 @@ void tst_QRegularExpression::captureNames() QFETCH(QString, pattern); QFETCH(StringToIntMap, namedCapturesIndexMap); - const QRegularExpression re(pattern); + QRegularExpression re(pattern); + + if (forceOptimize) + re.optimize(); + QStringList namedCaptureGroups = re.namedCaptureGroups(); int namedCaptureGroupsCount = namedCaptureGroups.size(); @@ -1515,6 +1574,10 @@ void tst_QRegularExpression::pcreJitStackUsage() QFETCH(QString, subject); QRegularExpression re(pattern); + + if (forceOptimize) + re.optimize(); + QVERIFY(re.isValid()); QRegularExpressionMatch match = re.match(subject); consistencyCheck(match); @@ -1541,6 +1604,10 @@ void tst_QRegularExpression::regularExpressionMatch() QFETCH(QString, subject); QRegularExpression re(pattern); + + if (forceOptimize) + re.optimize(); + QVERIFY(re.isValid()); QRegularExpressionMatch match = re.match(subject); consistencyCheck(match); @@ -1580,5 +1647,7 @@ void tst_QRegularExpression::JOptionUsage() QRegularExpression re(pattern); if (isValid && JOptionUsed) QTest::ignoreMessage(QtWarningMsg, qPrintable(warningMessage.arg(pattern))); + if (forceOptimize) + re.optimize(); QCOMPARE(re.isValid(), isValid); }