diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-08-02 18:20:18 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-08-29 13:20:53 +0200 |
commit | cad5c89a0f0ebb24f17171fdbab8355832f0f415 (patch) | |
tree | 1e49190e51018b27296858e5e4248f29b1965617 /src/qml/qml/qqmlcustomparser.cpp | |
parent | d937a53cbcd72304ba1c613ed2d2dc2010542f7e (diff) |
QmlModels: Fix enum resolution in ListElement
There were two problems:
a, We need to allow recursion when querying for enums. Otherwise we
cannot find enums in the same document.
b, when the enum resolution is done in the validation phase, we cannot
query the current document's QQmlType for enums, yet, as it's not
finalized. However, the QQmlPropertyValidator has the
QQmlPropertyCache we're looking for in that case.
c, As a drive-by, fix the excessive conversions between QByteArray and
QString.
Pick-to: 6.4
Fixes: QTBUG-95864
Change-Id: If0d2687986e1483a27ce11373a204235b92a6efd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/qml/qqmlcustomparser.cpp')
-rw-r--r-- | src/qml/qml/qqmlcustomparser.cpp | 63 |
1 files changed, 50 insertions, 13 deletions
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index fba805f084..ca3985bd92 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -1,6 +1,7 @@ // 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 "qml/qqmlpropertyvalidator_p.h" #include "qqmlcustomparser_p.h" #include <private/qv4compileddata_p.h> @@ -81,7 +82,7 @@ void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const A valid \a ok must be provided, or the function will assert. */ -int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const +int QQmlCustomParser::evaluateEnum(const QString &script, bool *ok) const { Q_ASSERT_X(ok, "QQmlCustomParser::evaluateEnum", "ok must not be a null pointer"); *ok = false; @@ -91,7 +92,7 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const // * <TypeName>.<ScopedEnumName>.<EnumValue> auto nextDot = [&](int dot) { - const int nextDot = script.indexOf('.', dot + 1); + const int nextDot = script.indexOf(u'.', dot + 1); return (nextDot == script.length() - 1) ? -1 : nextDot; }; @@ -99,7 +100,7 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const if (dot == -1) return -1; - QString scope = QString::fromUtf8(script.left(dot)); + const QString scope = script.left(dot); if (scope != QLatin1String("Qt")) { if (imports.isNull()) @@ -108,23 +109,35 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const if (imports.isT1()) { QQmlImportNamespace *ns = nullptr; - if (!imports.asT1()->resolveType(scope, &type, nullptr, &ns)) + + // Pass &recursionDetected to resolveType because that implicitly allows recursion. + // This way we can find the QQmlType of the document we're currently validating. + bool recursionDetected = false; + + if (!imports.asT1()->resolveType( + scope, &type, nullptr, &ns, nullptr, + QQmlType::AnyRegistrationType, &recursionDetected)) { return -1; + } + if (!type.isValid() && ns != nullptr) { dot = nextDot(dot); - if (dot == -1 || !imports.asT1()->resolveType(QString::fromUtf8(script.left(dot)), - &type, nullptr, nullptr)) { + if (dot == -1 || !imports.asT1()->resolveType( + script.left(dot), &type, nullptr, nullptr, nullptr, + QQmlType::AnyRegistrationType, &recursionDetected)) { return -1; } } } else { - QQmlTypeNameCache::Result result = imports.asT2()->query(scope); + // Allow recursion so that we can find enums from the same document. + const QQmlTypeNameCache::Result result + = imports.asT2()->query<QQmlImport::AllowRecursion>(scope); if (result.isValid()) { type = result.type; } else if (result.importNamespace) { dot = nextDot(dot); if (dot != -1) - type = imports.asT2()->query(QString::fromUtf8(script.left(dot))).type; + type = imports.asT2()->query<QQmlImport::AllowRecursion>(script.left(dot)).type; } } @@ -133,19 +146,43 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const const int dot2 = nextDot(dot); const bool dot2Valid = (dot2 != -1); - QByteArray enumValue = script.mid(dot2Valid ? dot2 + 1 : dot + 1); - QByteArray scopedEnumName = (dot2Valid ? script.mid(dot + 1, dot2 - dot - 1) : QByteArray()); + const QString enumValue = script.mid(dot2Valid ? dot2 + 1 : dot + 1); + const QString scopedEnumName = dot2Valid ? script.mid(dot + 1, dot2 - dot - 1) : QString(); + + // If we're currently validating the same document, we won't be able to find its enums using + // the QQmlType. However, we do have the property cache already, and that one contains the + // enums. + const QUrl documentUrl = validator ? validator->documentSourceUrl() : QUrl(); + if (documentUrl.isValid() && documentUrl == type.sourceUrl()) { + const QQmlPropertyCache::ConstPtr rootCache = validator->rootPropertyCache(); + const int count = rootCache->qmlEnumCount(); + for (int ii = 0; ii < count; ++ii) { + const QQmlEnumData *enumData = rootCache->qmlEnum(ii); + if (!scopedEnumName.isEmpty() && scopedEnumName != enumData->name) + continue; + + for (int jj = 0; jj < enumData->values.count(); ++jj) { + const QQmlEnumValue value = enumData->values.at(jj); + if (value.namedValue == enumValue) { + *ok = true; + return value.value; + } + } + } + return -1; + } + if (!scopedEnumName.isEmpty()) return type.scopedEnumValue(engine, scopedEnumName, enumValue, ok); else - return type.enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok); + return type.enumValue(engine, enumValue, ok); } - QByteArray enumValue = script.mid(dot + 1); + const QString enumValue = script.mid(dot + 1); const QMetaObject *mo = &Qt::staticMetaObject; int i = mo->enumeratorCount(); while (i--) { - int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok); + int v = mo->enumerator(i).keyToValue(enumValue.toUtf8().constData(), ok); if (*ok) return v; } |