QmlCompiler: Do not miscompile ID lookups in invalid types

If we cannot resolve a type, we need to assume that all its properties
are components and assign separate contexts to all inner objects.
Otherwise, if one of them actually is, the attempt to resolve it at run
time will crash.

Pick-to: 6.8
Fixes: QTBUG-129281
Change-Id: Ic34b5308accdd93f6797ee39fcd56040cf86b1ce
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2024-09-24 13:49:42 +02:00
parent 724c48f60a
commit dea8e38d95
4 changed files with 64 additions and 3 deletions

View File

@ -610,6 +610,12 @@ void QQmlJSImportVisitor::processDefaultProperties()
const QQmlJSMetaProperty defaultProp = parentScope->property(defaultPropertyName);
auto propType = defaultProp.type();
const auto handleUnresolvedDefaultProperty = [&](const QQmlJSScope::ConstPtr &) {
// Since we don't know the property type, we need to assume it's QQmlComponent and that
// IDs from the inner scopes are inaccessible.
for (const QQmlJSScope::Ptr &scope : std::as_const(*it))
scope->setIsWrappedInImplicitComponent(true);
// Property type is not fully resolved we cannot tell any more than this
m_logger->log(QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
"missing an import.")
@ -735,9 +741,20 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
for (const PendingPropertyObjectBinding &objectBinding :
std::as_const(m_pendingPropertyObjectBindings)) {
const QString propertyName = objectBinding.name;
QQmlJSScope::ConstPtr childScope = objectBinding.childScope;
QQmlJSScope::Ptr childScope = objectBinding.childScope;
if (!isTypeResolved(objectBinding.scope)) // guarantees property lookup
const auto handleUnresolvedType = [&](const QQmlJSScope::ConstPtr &type) {
// Since we don't know the property type we need to assume that it's QQmlComponent and
// that IDs from the child scope are inaccessible outside of it.
childScope->setIsWrappedInImplicitComponent(true);
m_logger->log(QStringLiteral("Type %1 is used but it is not resolved")
.arg(getScopeName(type, type->scopeType())),
qmlUnresolvedType, type->sourceLocation());
};
// guarantees property lookup
if (!isTypeResolved(objectBinding.scope, handleUnresolvedType))
continue;
QQmlJSMetaProperty property = objectBinding.scope->property(propertyName);
@ -748,6 +765,11 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
continue;
}
const auto handleUnresolvedProperty = [&](const QQmlJSScope::ConstPtr &) {
// Since we don't know the property type we need to assume that it's QQmlComponent and
// that IDs from the child scope are inaccessible outside of it.
childScope->setIsWrappedInImplicitComponent(true);
// Property type is not fully resolved we cannot tell any more than this
m_logger->log(QStringLiteral("Property \"%1\" has incomplete type \"%2\". You may be "
"missing an import.")
@ -774,7 +796,7 @@ void QQmlJSImportVisitor::processPropertyBindingObjects()
continue;
}
objectBinding.childScope->setIsWrappedInImplicitComponent(
childScope->setIsWrappedInImplicitComponent(
causesImplicitComponentWrapping(property, childScope));
// unique because it's per-scope and per-property

View File

@ -167,6 +167,7 @@ set(qml_files
indirectlyShadowable.qml
infinities.qml
infinitiesToInt.qml
insertContextOnInvalidType.qml
intEnumCompare.qml
intOverflow.qml
intToEnum.qml

View File

@ -0,0 +1,7 @@
import QtQml
import Handlerei
HandleHandler {
property var handleDelegateOutter: handleDelegate
handle: QtObject { id: handleDelegate }
}

View File

@ -130,6 +130,7 @@ private slots:
void infinities();
void infinitiesToInt();
void innerObjectNonShadowable();
void insertContextOnInvalidType();
void intEnumCompare();
void intOverflow();
void intToEnum();
@ -2475,6 +2476,36 @@ void tst_QmlCppCodegen::innerObjectNonShadowable()
QCOMPARE(rootObject->objectName(), u"foo"_s);
}
class HandleHandler : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlComponent *handle MEMBER m_handle)
private:
QQmlComponent *m_handle = nullptr;
};
void tst_QmlCppCodegen::insertContextOnInvalidType()
{
qmlRegisterType<HandleHandler>("Handlerei", 1, 0, "HandleHandler");
QQmlEngine engine;
QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/insertContextOnInvalidType.qml"_s));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QTest::ignoreMessage(
QtWarningMsg,
"qrc:/qt/qml/TestTypes/insertContextOnInvalidType.qml:5: "
"ReferenceError: handleDelegate is not defined");
QScopedPointer<QObject> rootObject(component.create());
QVERIFY(rootObject);
const char *outter = "handleDelegateOutter";
QVERIFY(rootObject->metaObject()->indexOfProperty(outter) != -1);
QVERIFY(!rootObject->property(outter).isValid());
}
void tst_QmlCppCodegen::intEnumCompare()
{
QQmlEngine engine;