diff options
Diffstat (limited to 'src/qml/jsruntime/qv4regexpobject.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject.cpp | 206 |
1 files changed, 75 insertions, 131 deletions
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index c1a42c4afa..144cd39bcd 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -1,46 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module 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 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4regexpobject_p.h" -#include "qv4objectproto_p.h" #include "qv4regexp_p.h" -#include "qv4stringobject_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4jscall_p.h" @@ -49,7 +11,6 @@ #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> -#include <QtCore/qregexp.h> #if QT_CONFIG(regularexpression) #include <QtCore/qregularexpression.h> #endif @@ -60,8 +21,6 @@ QT_BEGIN_NAMESPACE -Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); - using namespace QV4; DEFINE_OBJECT_VTABLE(RegExpObject); @@ -84,57 +43,40 @@ void Heap::RegExpObject::init(QV4::RegExp *value) o->initProperties(); } -// Converts a QRegExp to a JS RegExp. -// The conversion is not 100% exact since ECMA regexp and QRegExp -// have different semantics/flags, but we try to do our best. -void Heap::RegExpObject::init(const QRegExp &re) -{ - Object::init(); - - // Convert the pattern to a ECMAScript pattern. - QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax()); - if (re.isMinimal()) { - QString ecmaPattern; - int len = pattern.length(); - ecmaPattern.reserve(len); - int i = 0; - const QChar *wc = pattern.unicode(); - bool inBracket = false; - while (i < len) { - QChar c = wc[i++]; - ecmaPattern += c; - switch (c.unicode()) { - case '?': - case '+': - case '*': - case '}': - if (!inBracket) - ecmaPattern += QLatin1Char('?'); - break; - case '\\': - if (i < len) - ecmaPattern += wc[i++]; - break; - case '[': - inBracket = true; - break; - case ']': - inBracket = false; - break; - default: - break; - } +static QString minimalPattern(const QString &pattern) +{ + QString ecmaPattern; + int len = pattern.size(); + ecmaPattern.reserve(len); + int i = 0; + const QChar *wc = pattern.unicode(); + bool inBracket = false; + while (i < len) { + QChar c = wc[i++]; + ecmaPattern += c; + switch (c.unicode()) { + case '?': + case '+': + case '*': + case '}': + if (!inBracket) + ecmaPattern += QLatin1Char('?'); + break; + case '\\': + if (i < len) + ecmaPattern += wc[i++]; + break; + case '[': + inBracket = true; + break; + case ']': + inBracket = false; + break; + default: + break; } - pattern = ecmaPattern; } - - Scope scope(internalClass->engine); - Scoped<QV4::RegExpObject> o(scope, this); - - uint flags = (re.caseSensitivity() == Qt::CaseInsensitive ? CompiledData::RegExp::RegExp_IgnoreCase : CompiledData::RegExp::RegExp_NoFlags); - o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags)); - - o->initProperties(); + return ecmaPattern; } #if QT_CONFIG(regularexpression) @@ -148,10 +90,16 @@ void Heap::RegExpObject::init(const QRegularExpression &re) 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)); + QRegularExpression::PatternOptions options = re.patternOptions(); + uint flags = (options & QRegularExpression::CaseInsensitiveOption) + ? CompiledData::RegExp::RegExp_IgnoreCase + : CompiledData::RegExp::RegExp_NoFlags; + if (options & QRegularExpression::MultilineOption) + flags |= CompiledData::RegExp::RegExp_Multiline; + QString pattern = re.pattern(); + if (options & QRegularExpression::InvertedGreedinessOption) + pattern = minimalPattern(pattern); + o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags)); o->initProperties(); } #endif @@ -163,26 +111,18 @@ void RegExpObject::initProperties() Q_ASSERT(value()); } -// Converts a JS RegExp to a QRegExp. -// The conversion is not 100% exact since ECMA regexp and QRegExp -// have different semantics/flags, but we try to do our best. -QRegExp RegExpObject::toQRegExp() const -{ - Qt::CaseSensitivity caseSensitivity = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) ? Qt::CaseInsensitive : Qt::CaseSensitive; - 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); + QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption; + if (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) + options |= QRegularExpression::CaseInsensitiveOption; + if (value()->flags & CompiledData::RegExp::RegExp_Multiline) + options |= QRegularExpression::MultilineOption; + return QRegularExpression(*value()->pattern, options); } #endif @@ -193,7 +133,7 @@ QString RegExpObject::toString() const p = QStringLiteral("(?:)"); } else { // escape certain parts, see ch. 15.10.4 - p.replace('/', QLatin1String("\\/")); + p.replace(u'/', QLatin1String("\\/")); } return p; } @@ -204,7 +144,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s Scope scope(engine); int offset = (global() || sticky()) ? lastIndex() : 0; - if (offset < 0 || offset > s.length()) { + if (offset < 0 || offset > s.size()) { setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -228,7 +168,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s int len = value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); - int strlen = s.length(); + int strlen = s.size(); for (int i = 0; i < len; ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; @@ -255,9 +195,9 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s DEFINE_OBJECT_VTABLE(RegExpCtor); -void Heap::RegExpCtor::init(QV4::ExecutionContext *scope) +void Heap::RegExpCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("RegExp")); + Heap::FunctionObject::init(engine, QStringLiteral("RegExp")); clearLastMatch(); } @@ -290,7 +230,7 @@ uint parseFlags(Scope &scope, const QV4::Value *f) if (scope.hasException()) return flags; QString str = s->toQString(); - for (int i = 0; i < str.length(); ++i) { + for (int i = 0; i < str.size(); ++i) { if (str.at(i) == QLatin1Char('g') && !(flags & CompiledData::RegExp::RegExp_Global)) { flags |= CompiledData::RegExp::RegExp_Global; } else if (str.at(i) == QLatin1Char('i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) { @@ -440,7 +380,7 @@ ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Val QString s = str->toQString(); int offset = r->lastIndex(); - if (offset < 0 || offset > s.length()) { + if (offset < 0 || offset > s.size()) { r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -481,6 +421,8 @@ ReturnedValue RegExpPrototype::exec(ExecutionEngine *engine, const Object *o, co ScopedFunctionObject exec(scope, o->get(key)); if (exec) { ScopedValue result(scope, exec->call(o, s, 1)); + if (scope.hasException()) + RETURN_UNDEFINED(); if (!result->isNull() && !result->isObject()) return scope.engine->throwTypeError(); return result->asReturnedValue(); @@ -498,7 +440,7 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value if (!r) return scope.engine->throwTypeError(); - ScopedValue arg(scope, argc ? argv[0]: Value::undefinedValue()); + ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue()); ScopedString str(scope, arg->toString(scope.engine)); if (scope.hasException()) RETURN_UNDEFINED(); @@ -574,7 +516,7 @@ ReturnedValue RegExpPrototype::method_get_ignoreCase(const FunctionObject *f, co static int advanceStringIndex(int index, const QString &str, bool unicode) { if (unicode) { - if (index < str.length() - 1 && + if (index < str.size() - 1 && str.at(index).isHighSurrogate() && str.at(index + 1).isLowSurrogate()) ++index; @@ -663,7 +605,7 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); - int lengthS = s->toQString().length(); + int lengthS = s->toQString().size(); ScopedString replaceValue(scope); ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue())); @@ -715,7 +657,7 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); QString m = matchString->toQString(); - int matchLength = m.length(); + int matchLength = m.size(); v = resultObject->get(scope.engine->id_index()); int position = v->toInt32(); position = qMax(qMin(position, lengthS), 0); @@ -724,19 +666,21 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val int n = 1; Scope innerScope(scope.engine); - JSCallData cData(scope, nCaptures + 3); + JSCallArguments cData(scope, nCaptures + 3); while (n <= nCaptures) { v = resultObject->get(PropertyKey::fromArrayIndex(n)); if (!v->isUndefined()) - cData->args[n] = v->toString(scope.engine); + cData.args[n] = v->toString(scope.engine); ++n; } QString replacement; if (functionalReplace) { - cData->args[0] = matchString; - cData->args[nCaptures + 1] = Encode(position); - cData->args[nCaptures + 2] = s; + cData.args[0] = matchString; + cData.args[nCaptures + 1] = Encode(position); + cData.args[nCaptures + 2] = s; ScopedValue replValue(scope, replaceFunction->call(cData)); + if (scope.hasException()) + return Encode::undefined(); replacement = replValue->toQString(); } else { replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString()); @@ -744,12 +688,12 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); if (position >= nextSourcePosition) { - accumulatedResult += s->toQString().midRef(nextSourcePosition, position - nextSourcePosition) + replacement; + accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition, position - nextSourcePosition) + replacement; nextSourcePosition = position + matchLength; } } if (nextSourcePosition < lengthS) { - accumulatedResult += s->toQString().midRef(nextSourcePosition); + accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition); } return scope.engine->newString(accumulatedResult)->asReturnedValue(); } @@ -840,7 +784,7 @@ ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value return A->asReturnedValue(); QString S = s->toQString(); - int size = S.length(); + int size = S.size(); if (size == 0) { ScopedValue z(scope, exec(scope.engine, splitter, s)); if (z->isNull()) @@ -961,8 +905,8 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val return scope.engine->throwTypeError(); Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc)); - - r->d()->value.set(scope.engine, re->value()); + if (re) // Otherwise the regexp constructor should have thrown an exception + r->d()->value.set(scope.engine, re->value()); return Encode::undefined(); } |