diff options
-rw-r--r-- | src/3rdparty/masm/yarr/YarrJIT.cpp | 8 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexp.cpp | 60 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject.cpp | 8 | ||||
-rw-r--r-- | tests/auto/qml/qml.pro | 1 | ||||
-rw-r--r-- | tests/auto/qml/qv4regexp/qv4regexp.pro | 8 | ||||
-rw-r--r-- | tests/auto/qml/qv4regexp/tst_qv4regexp.cpp | 53 |
6 files changed, 114 insertions, 24 deletions
diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp index 0655bb0a70..dee2ade407 100644 --- a/src/3rdparty/masm/yarr/YarrJIT.cpp +++ b/src/3rdparty/masm/yarr/YarrJIT.cpp @@ -3531,14 +3531,14 @@ public: if (compileMode == MatchOnly) { if (m_charSize == Char8) - codeBlock.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, "YarJIT", "Match-only 8-bit regular expression")); + codeBlock.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, "YarrJIT", "Match-only 8-bit regular expression")); else - codeBlock.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, "YarJIT", "Match-only 16-bit regular expression")); + codeBlock.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, "YarrJIT", "Match-only 16-bit regular expression")); } else { if (m_charSize == Char8) - codeBlock.set8BitCode(FINALIZE_CODE(linkBuffer, "YarJIT", "8-bit regular expression")); + codeBlock.set8BitCode(FINALIZE_CODE(linkBuffer, "YarrJIT", "8-bit regular expression")); else - codeBlock.set16BitCode(FINALIZE_CODE(linkBuffer, "YarJIT", "16-bit regular expression")); + codeBlock.set16BitCode(FINALIZE_CODE(linkBuffer, "YarrJIT", "16-bit regular expression")); } if (m_failureReason) codeBlock.setFallBackWithFailureReason(*m_failureReason); diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 05c3b4c4ca..4ed1dbd5aa 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -45,6 +45,22 @@ using namespace QV4; +static JSC::RegExpFlags jscFlags(uint flags) +{ + JSC::RegExpFlags jscFlags = JSC::NoFlags; + if (flags & CompiledData::RegExp::RegExp_Global) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal); + if (flags & CompiledData::RegExp::RegExp_IgnoreCase) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase); + if (flags & CompiledData::RegExp::RegExp_Multiline) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline); + if (flags & CompiledData::RegExp::RegExp_Unicode) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode); + if (flags & CompiledData::RegExp::RegExp_Sticky) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagSticky); + return jscFlags; +} + RegExpCache::~RegExpCache() { for (RegExpCache::Iterator it = begin(), e = end(); it != e; ++it) { @@ -57,21 +73,43 @@ DEFINE_MANAGED_VTABLE(RegExp); uint RegExp::match(const QString &string, int start, uint *matchOffsets) { + static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1; + if (!isValid()) return JSC::Yarr::offsetNoMatch; WTF::String s(string); #if ENABLE(YARR_JIT) - if (d()->hasValidJITCode()) { + auto *priv = d(); + if (priv->hasValidJITCode()) { + uint ret = JSC::Yarr::offsetNoMatch; #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) char buffer[8192]; - return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets, buffer, 8192).start); + ret = uint(priv->jitCode->execute(s.characters16(), start, s.length(), + (int*)matchOffsets, buffer, 8192).start); #else - return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets).start); + ret = uint(priv->jitCode->execute(s.characters16(), start, s.length(), + (int*)matchOffsets).start); #endif + if (ret != offsetJITFail) + return ret; + + // JIT failed. We need byteCode to run the interpreter. + if (!priv->byteCode) { + JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(*priv->pattern), jscFlags(priv->flags), + error); + + // As we successfully parsed the pattern before, we should still be able to. + Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError); + + priv->byteCode = JSC::Yarr::byteCompile( + yarrPattern, + priv->internalClass->engine->bumperPointerAllocator).release(); + } } -#endif +#endif // ENABLE(YARR_JIT) return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets); } @@ -176,19 +214,7 @@ void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, uint fl valid = false; JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; - JSC::RegExpFlags jscFlags = JSC::NoFlags; - if (flags & CompiledData::RegExp::RegExp_Global) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal); - if (flags & CompiledData::RegExp::RegExp_IgnoreCase) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase); - if (flags & CompiledData::RegExp::RegExp_Multiline) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline); - if (flags & CompiledData::RegExp::RegExp_Unicode) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode); - if (flags & CompiledData::RegExp::RegExp_Sticky) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagSticky); - - JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags, error); + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags(flags), error); if (error != JSC::Yarr::ErrorCode::NoError) return; subPatternCount = yarrPattern.m_numSubpatterns; diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 9df286065d..39a2e96b45 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -181,17 +181,19 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s } Q_ALLOCA_VAR(uint, matchOffsets, value()->captureCount() * 2 * sizeof(uint)); - const int result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets); + const uint result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets); RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor()); regExpCtor->d()->clearLastMatch(); - if (result == -1) { + if (result == JSC::Yarr::offsetNoMatch) { if (global() || sticky()) setLastIndex(0); RETURN_RESULT(Encode::null()); } + Q_ASSERT(result <= uint(std::numeric_limits<int>::max())); + // fill in result data ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray))); int len = value()->captureCount(); @@ -207,7 +209,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s array->arrayPut(i, v); } array->setArrayLengthUnchecked(len); - array->setProperty(Index_ArrayIndex, Value::fromInt32(result)); + array->setProperty(Index_ArrayIndex, Value::fromInt32(int(result))); array->setProperty(Index_ArrayInput, *str); RegExpCtor::Data *dd = regExpCtor->d(); diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 05d63daedf..7cfddf0eaf 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -73,6 +73,7 @@ PRIVATETESTS += \ qv4assembler \ qv4mm \ qv4identifiertable \ + qv4regexp \ ecmascripttests \ bindingdependencyapi \ v4misc diff --git a/tests/auto/qml/qv4regexp/qv4regexp.pro b/tests/auto/qml/qv4regexp/qv4regexp.pro new file mode 100644 index 0000000000..f4ab6941f5 --- /dev/null +++ b/tests/auto/qml/qv4regexp/qv4regexp.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qv4regexp +macos:CONFIG -= app_bundle + +SOURCES += tst_qv4regexp.cpp + +QT += qml qml-private testlib + diff --git a/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp new file mode 100644 index 0000000000..cbf9c3e47d --- /dev/null +++ b/tests/auto/qml/qv4regexp/tst_qv4regexp.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QtQml/qjsengine.h> + +class tst_qv4regexp : public QObject +{ + Q_OBJECT + +private slots: + void catchJitFail(); +}; + +void tst_qv4regexp::catchJitFail() +{ + QJSEngine engine; + QJSValue result = engine.evaluate(QLatin1String( + "var prevString = \" ok\";" + "var r = /^(\\s*)(([\\)\\]}]?\\s*)*([\\)\\]]\\s*))?;/.exec(prevString);" + "r === null;"), QLatin1String("regexptest.js")); + QVERIFY(result.isBool()); + QVERIFY(result.toBool()); +} + +QTEST_MAIN(tst_qv4regexp) + +#include "tst_qv4regexp.moc" |