aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-08-23 10:42:35 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-08-31 17:39:15 +0200
commit03a3d5a308bdd86d19e5cede16d9e5764a0d82f8 (patch)
tree38cf5dc0b61930c4efc27726764a7d8dc71c5166
parentc466c59e27eea75ac97b1b1a7057afa5e6f3ed55 (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.cpp123
-rw-r--r--src/qmlcompiler/qqmljstyperesolver_p.h5
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/failures.qml4
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/gadgetwithenum.h23
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