diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2022-08-23 10:42:35 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2022-08-31 17:39:15 +0200 |
commit | 03a3d5a308bdd86d19e5cede16d9e5764a0d82f8 (patch) | |
tree | 38cf5dc0b61930c4efc27726764a7d8dc71c5166 | |
parent | c466c59e27eea75ac97b1b1a7057afa5e6f3ed55 (diff) |
QmlCompiler: Prevent lookup of value type where we need an object type
With a particular nefarious combination of Q_GADGET and inheritance from
QObject you can make QmlCompiler believe a type is a value type even
though it is actually an object type. We never want to touch such a
thing.
There was a safe guard against this when looking up the type from the
scope, but by putting it in a type namespace you could circumvent it.
Refactor the code to apply to both cases the same way.
Fixes: QTBUG-104556
Fixes: QTBUG-105608
Change-Id: I8a690e2b6f78fcaba0911a93504cde0d2c7dde0d
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 0a8fe228f6bb65afe08f1bc203653266fa204ba5)
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver.cpp | 123 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljstyperesolver_p.h | 5 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/failures.qml | 4 | ||||
-rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h | 23 |
5 files changed, 98 insertions, 58 deletions
diff --git a/src/qmlcompiler/qqmljstyperesolver.cpp b/src/qmlcompiler/qqmljstyperesolver.cpp index 7b9893d5aa..8afc2e6a2c 100644 --- a/src/qmlcompiler/qqmljstyperesolver.cpp +++ b/src/qmlcompiler/qqmljstyperesolver.cpp @@ -316,6 +316,65 @@ QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const return {}; } +QQmlJSRegisterContent QQmlJSTypeResolver::referenceTypeForName( + const QString &name, const QQmlJSScope::ConstPtr &scopeType, + bool hasObjectModulePrefix) const +{ + QQmlJSScope::ConstPtr type = typeForName(name); + if (!type) + return QQmlJSRegisterContent(); + + if (type->isSingleton()) + return QQmlJSRegisterContent::create(storedType(type), type, + QQmlJSRegisterContent::Singleton, scopeType); + + if (type->isScript()) + return QQmlJSRegisterContent::create(storedType(type), type, + QQmlJSRegisterContent::Script, scopeType); + + if (const auto attached = type->attachedType()) { + if (!genericType(attached)) { + m_logger->logWarning(u"Cannot resolve generic base of attached %1"_qs.arg( + attached->internalName()), + Log_Compiler, attached->sourceLocation()); + return {}; + } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { + m_logger->logWarning( + u"Cannot retrieve attached object for non-reference type %1"_qs.arg( + type->internalName()), + Log_Compiler, type->sourceLocation()); + return {}; + } else { + // We don't know yet whether we need the attached or the plain object. In direct + // mode, we will figure this out using the scope type and access any enums of the + // plain type directly. In indirect mode, we can use enum lookups. + return QQmlJSRegisterContent::create( + storedType(attached), attached, + hasObjectModulePrefix + ? QQmlJSRegisterContent::ObjectAttached + : QQmlJSRegisterContent::ScopeAttached, type); + } + } + + switch (type->accessSemantics()) { + case QQmlJSScope::AccessSemantics::None: + case QQmlJSScope::AccessSemantics::Reference: + // A plain reference to a non-singleton, non-attached type. + // We may still need the plain type reference for enum lookups, + // Store it as QMetaObject. + // This only works with namespaces and object types. + return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(), + QQmlJSRegisterContent::MetaType, type); + case QQmlJSScope::AccessSemantics::Sequence: + case QQmlJSScope::AccessSemantics::Value: + // This is not actually a type reference. You cannot get the metaobject + // of a value type in QML and sequences don't even have metaobjects. + break; + } + + return QQmlJSRegisterContent(); +} + QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &container) const { QQmlJSScope::ConstPtr type; @@ -645,45 +704,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr } } - if (QQmlJSScope::ConstPtr type = typeForName(name)) { - if (type->isSingleton()) - return QQmlJSRegisterContent::create(storedType(type), type, - QQmlJSRegisterContent::Singleton); - - if (type->isScript()) - return QQmlJSRegisterContent::create(storedType(type), type, - QQmlJSRegisterContent::Script); - - if (const auto attached = type->attachedType()) { - if (!genericType(attached)) { - m_logger->logWarning(u"Cannot resolve generic base of attached %1"_qs.arg( - attached->internalName()), - Log_Compiler); - return {}; - } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { - m_logger->logWarning( - u"Cannot retrieve attached object for non-reference type %1"_qs.arg( - type->internalName()), - Log_Compiler); - return {}; - } else { - // We don't know yet whether we need the attached or the plain object. In direct - // mode, we will figure this out using the scope type and access any enums of the - // plain type directly. In indirect mode, we can use enum lookups. - return QQmlJSRegisterContent::create(storedType(attached), attached, - QQmlJSRegisterContent::ScopeAttached, type); - } - } - - // A plain reference to a non-singleton, non-attached type. - // If it's undefined, we can actually get an "instance" of it. - // Therefore, use a primitive value to store it. - // Otherwise this is a plain type reference without instance. - // We may still need the plain type reference for enum lookups, - // so store it in QJSValue for now. - return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(), - QQmlJSRegisterContent::MetaType, type); - } + QQmlJSRegisterContent result = referenceTypeForName(name); + if (result.isValid()) + return result; if (m_jsGlobalObject->hasProperty(name)) { return QQmlJSRegisterContent::create(jsValueType(), m_jsGlobalObject->property(name), @@ -871,25 +894,9 @@ QQmlJSRegisterContent QQmlJSTypeResolver::memberType(const QQmlJSRegisterContent return {}; } - if (QQmlJSScope::ConstPtr result = typeForName(name)) { - QQmlJSScope::ConstPtr attached = result->attachedType(); - if (attached && genericType(attached)) { - return QQmlJSRegisterContent::create(storedType(attached), attached, - QQmlJSRegisterContent::ObjectAttached, - result); - } - - if (result->isSingleton()) { - return QQmlJSRegisterContent::create( - storedType(result), result, - QQmlJSRegisterContent::Singleton, type.scopeType()); - } - - return QQmlJSRegisterContent::create(metaObjectType(), metaObjectType(), - QQmlJSRegisterContent::MetaType, result); - } - - return {}; + return referenceTypeForName( + name, type.scopeType(), + type.variant() == QQmlJSRegisterContent::ObjectModulePrefix); } Q_UNREACHABLE(); diff --git a/src/qmlcompiler/qqmljstyperesolver_p.h b/src/qmlcompiler/qqmljstyperesolver_p.h index c6a277d915..c2c80166c2 100644 --- a/src/qmlcompiler/qqmljstyperesolver_p.h +++ b/src/qmlcompiler/qqmljstyperesolver_p.h @@ -158,6 +158,11 @@ protected: QQmlJSRegisterContent *result, BaseOrExtension mode) const; QQmlJSRegisterContent lengthProperty(bool isWritable, const QQmlJSScope::ConstPtr &scope) const; + QQmlJSRegisterContent referenceTypeForName( + const QString &name, + const QQmlJSScope::ConstPtr &scopeType = QQmlJSScope::ConstPtr(), + bool hasObjectModuelPrefix = false) const; + QQmlJSScope::ConstPtr m_voidType; QQmlJSScope::ConstPtr m_numberPrototype; QQmlJSScope::ConstPtr m_realType; diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 96d08c857b..5f7dbba8d7 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -3,6 +3,7 @@ set(cpp_sources birthdayparty.cpp birthdayparty.h cppbaseclass.h dynamicmeta.h + gadgetwithenum.h invisible.h objectwithmethod.h person.cpp person.h diff --git a/tests/auto/qml/qmlcppcodegen/data/failures.qml b/tests/auto/qml/qmlcppcodegen/data/failures.qml index 8d301bab7f..b8331c0ad4 100644 --- a/tests/auto/qml/qmlcppcodegen/data/failures.qml +++ b/tests/auto/qml/qmlcppcodegen/data/failures.qml @@ -1,5 +1,6 @@ import QtQml import TestTypes +import TestTypes as TT2 import Ambiguous 1.2 QtObject { @@ -32,4 +33,7 @@ QtObject { signal bar() // Cannot assign potential undefined onFoo: objectName = self.bar() + + property int enumFromGadget1: GadgetWithEnum.CONNECTED + 1 + property int enumFromGadget2: TT2.GadgetWithEnum.CONNECTED + 1 } diff --git a/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h new file mode 100644 index 0000000000..d146b9f654 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h @@ -0,0 +1,23 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef GADGETWITHENUM_H +#define GADGETWITHENUM_H + +#include <QtCore/qobject.h> +#include <QtQmlIntegration/qqmlintegration.h> + +class GadgetWithEnum : public QObject { + Q_GADGET + QML_ELEMENT + +public: + enum State { + DISCONNECTED, + CONNECTING, + CONNECTED + }; + Q_ENUM(State) +}; + +#endif // GADGETWITHENUM_H |