diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-09-05 16:41:11 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-09-13 15:10:49 +0200 |
commit | 28eb0caec23e0c315056cb035535b8806ccb0f37 (patch) | |
tree | ef17b4cf984a04c94804cae29e5b546f37060f87 | |
parent | 122d0367fd96526b3573a3188286153c846680ca (diff) |
Force creation of metaobjects for top level objects and components
We want those to be different types, even if they don't add any
properties, signals etc. Otherwise you can cross-assign objects of
components defined in different files.
Fully-dynamic types, such as QQmlPropertyMap are still an exception to
this. We need to use the original meta object so that you can still
dynamically add properties to any derived types. Therefore, all types
derived from QQmlPropertyMap are in fact just aliases of each other.
Also, types which aren't addressable from the outside don't get their
own meta object. Types are addressable if they live in files and the
file name starts with an uppercase character. Otherwise there is no way
to refer to the component from anywhere else in QML.
Fixes: QTBUG-76021
Change-Id: I96a01fdad13e50e4705520fec46f2b3373e0c365
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
8 files changed, 66 insertions, 13 deletions
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index def4480198..94bf3cbdc3 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -152,9 +152,18 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil template <typename ObjectContainer> inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) { + auto isAddressable = [](const QUrl &url) { + const QString fileName = url.fileName(); + return !fileName.isEmpty() && fileName.front().isUpper(); + }; + const CompiledObject *obj = objectContainer->objectAt(objectIndex); + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 + || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0 + || (((obj->flags & QV4::CompiledData::Object::IsComponent) + || (objectIndex == 0 && isAddressable(objectContainer->url()))) + && !objectContainer->resolvedType(obj->inheritedTypeNameIndex)->isFullyDynamicType); - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0; if (!needVMEMetaObject) { auto binding = obj->bindingsBegin(); auto end = obj->bindingsEnd(); diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 79ec507388..1d2fa42b75 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -222,12 +222,12 @@ void tst_qqmlcomponent::qmlCreateObjectAutoParent() QVERIFY(window_item); QVERIFY(window_window); - QCOMPARE(qtobject_item->metaObject()->className(), "QQuickItem"); - QCOMPARE(qtobject_window->metaObject()->className(), "QQuickWindow"); - QCOMPARE(item_item->metaObject()->className(), "QQuickItem"); - QCOMPARE(item_window->metaObject()->className(), "QQuickWindow"); - QCOMPARE(window_item->metaObject()->className(), "QQuickItem"); - QCOMPARE(window_window->metaObject()->className(), "QQuickWindow"); + QVERIFY(QByteArray(qtobject_item->metaObject()->className()).startsWith("QQuickItem")); + QVERIFY(QByteArray(qtobject_window->metaObject()->className()).startsWith("QQuickWindow")); + QVERIFY(QByteArray(item_item->metaObject()->className()).startsWith("QQuickItem")); + QVERIFY(QByteArray(item_window->metaObject()->className()).startsWith("QQuickWindow")); + QVERIFY(QByteArray(window_item->metaObject()->className()).startsWith("QQuickItem")); + QVERIFY(QByteArray(window_window->metaObject()->className()).startsWith("QQuickWindow")); QCOMPARE(qtobject_qtobject->parent(), qtobjectParent); QCOMPARE(qtobject_item->parent(), qtobjectParent); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 8adacd8829..007fb5cef2 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2500,7 +2500,15 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QCOMPARE(QString(object->metaObject()->className()), type); + const QMetaObject *meta = object->metaObject(); + for (; meta; meta = meta->superClass()) { + const QString className(meta->className()); + if (!className.contains("_QMLTYPE_") && !className.contains("_QML_")) { + QCOMPARE(className, type); + break; + } + } + QVERIFY(meta != nullptr); } engine.setImportPathList(defaultImportPathList); @@ -4043,10 +4051,12 @@ void tst_qqmllanguage::implicitImportsLast() VERIFY_ERRORS(0); QScopedPointer<QObject> object(component.create()); QVERIFY(object != nullptr); - QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea"))); + QVERIFY(QString(object->metaObject()->superClass()->superClass()->className()) + .startsWith(QLatin1String("QQuickMouseArea"))); QObject* object2 = object->property("item").value<QObject*>(); QVERIFY(object2 != nullptr); - QCOMPARE(QString(object2->metaObject()->className()), QLatin1String("QQuickRectangle")); + QCOMPARE(QString(object2->metaObject()->superClass()->className()), + QLatin1String("QQuickRectangle")); engine.setImportPathList(defaultImportPathList); } @@ -5030,7 +5040,6 @@ void tst_qqmllanguage::instanceof() if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") || QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle")) - QEXPECT_FAIL("", "QTBUG-58477: QML type rules are a little lax", Continue); QCOMPARE(returnValue, expectedValue.toBool()); } else { QVERIFY(expr.hasError()); diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index 67da768f73..bc407e97a2 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -35,6 +35,9 @@ #include <private/qqmlboundsignal_p.h> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif #include <QtCore/private/qobject_p.h> #include "../../shared/util.h" @@ -2015,9 +2018,13 @@ void tst_qqmlproperty::warnOnInvalidBinding() expectedWarning = testUrl.toString() + QString::fromLatin1(":7:5: Unable to assign QQuickText to QQuickRectangle"); QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); +#if QT_CONFIG(regularexpression) // V8 error message for invalid binding to anchor - expectedWarning = testUrl.toString() + QString::fromLatin1(":14:9: Unable to assign QQuickItem_QML_8 to QQuickAnchorLine"); - QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + const QRegularExpression warning( + "^" + testUrl.toString() + + ":14:9: Unable to assign QQuickItem_QML_\\d+ to QQuickAnchorLine$"); + QTest::ignoreMessage(QtWarningMsg, warning); +#endif QQmlComponent component(&engine, testUrl); QObject *obj = component.create(); diff --git a/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml b/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml new file mode 100644 index 0000000000..9559bc0b5f --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/SpecialObject1.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + readonly property bool fakeProperty: false +} diff --git a/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml b/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml new file mode 100644 index 0000000000..ed4ad04fef --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/SpecialObject2.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + objectName: "special" +} diff --git a/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml b/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml new file mode 100644 index 0000000000..5e1ea233b9 --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/noDuckType.qml @@ -0,0 +1,7 @@ +import QtQml 2.9 + +QtObject { + property SpecialObject1 obj1: SpecialObject1 {} + property SpecialObject2 obj2: SpecialObject2 {} + property string result: (obj1 instanceof SpecialObject2) ? "bad" : "good" +} diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 9a1e4667dd..c9e92cd3c9 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -54,6 +54,7 @@ private slots: void metaObjectSize_data(); void metaObjectSize(); void metaObjectChecksum(); + void metaObjectsForRootElements(); private: QQmlEngine engine; @@ -543,4 +544,14 @@ void tst_qqmlpropertycache::metaObjectChecksum() } } +void tst_qqmlpropertycache::metaObjectsForRootElements() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("noDuckType.qml")); + QVERIFY(c.isReady()); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("result").toString(), QString::fromLatin1("good")); +} + QTEST_MAIN(tst_qqmlpropertycache) |