From 3af36ba6eb58b2f455e015579540baa51e0c5c26 Mon Sep 17 00:00:00 2001 From: Kari Oikarinen Date: Thu, 21 Mar 2019 09:08:25 +0200 Subject: Bump version Change-Id: Idf8a6ab90f22956444004c775d9b789d567671ad --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index b0541fe720..a3c853d760 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.12.2 +MODULE_VERSION = 5.12.3 -- cgit v1.2.3 From c091f3f4b4889ac6be26e018c5e8b673adee7c47 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 18 Mar 2019 11:49:05 +0100 Subject: V4: Do not invert non-reflexive comparison binops This is only useful for the few (4) comparisons where we have specialized instructions, and it's very error-prone. Change-Id: I37efe94f54ba0adf393d9236df2d13aa6685eb46 Fixes: QTBUG-74476 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 85 +++++++++++++----------------- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 10 ++++ 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b0bec5b6f2..3fdba08f20 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1738,59 +1738,46 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re return Reference::fromAccumulator(this); } -static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper) -{ - switch (oper) { - case QSOperator::StrictEqual: return QSOperator::StrictEqual; - case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual; - case QSOperator::Equal: return QSOperator::Equal; - case QSOperator::NotEqual: return QSOperator::NotEqual; - case QSOperator::Gt: return QSOperator::Le; - case QSOperator::Ge: return QSOperator::Lt; - case QSOperator::Lt: return QSOperator::Ge; - case QSOperator::Le: return QSOperator::Gt; - default: Q_UNIMPLEMENTED(); return QSOperator::Invalid; - } -} - Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right) { - if (left.isConstant()) { - oper = operatorForSwappedOperands(oper); - qSwap(left, right); - } + // See if we can generate specialized comparison instructions: + if (oper == QSOperator::Equal || oper == QSOperator::NotEqual) { + // Because == and != are reflexive, we can do the following: + if (left.isConstant() && !right.isConstant()) + qSwap(left, right); // null==a -> a==null - if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) { - Value c = Value::fromReturnedValue(right.constant); - if (c.isNull() || c.isUndefined()) { - left.loadInAccumulator(); - if (oper == QSOperator::Equal) { - Instruction::CmpEqNull cmp; - bytecodeGenerator->addInstruction(cmp); - addCJump(); - return Reference(); - } else if (oper == QSOperator::NotEqual) { - Instruction::CmpNeNull cmp; - bytecodeGenerator->addInstruction(cmp); - addCJump(); - return Reference(); - } - } else if (c.isInt32()) { - left.loadInAccumulator(); - if (oper == QSOperator::Equal) { - Instruction::CmpEqInt cmp; - cmp.lhs = c.int_32(); - bytecodeGenerator->addInstruction(cmp); - addCJump(); - return Reference(); - } else if (oper == QSOperator::NotEqual) { - Instruction::CmpNeInt cmp; - cmp.lhs = c.int_32(); - bytecodeGenerator->addInstruction(cmp); - addCJump(); - return Reference(); - } + if (right.isConstant()) { + Value c = Value::fromReturnedValue(right.constant); + if (c.isNull() || c.isUndefined()) { + left.loadInAccumulator(); + if (oper == QSOperator::Equal) { + Instruction::CmpEqNull cmp; + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } else if (oper == QSOperator::NotEqual) { + Instruction::CmpNeNull cmp; + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } + } else if (c.isInt32()) { + left.loadInAccumulator(); + if (oper == QSOperator::Equal) { + Instruction::CmpEqInt cmp; + cmp.lhs = c.int_32(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } else if (oper == QSOperator::NotEqual) { + Instruction::CmpNeInt cmp; + cmp.lhs = c.int_32(); + bytecodeGenerator->addInstruction(cmp); + addCJump(); + return Reference(); + } + } } } diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 42d8f37ca8..0491ccd473 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -235,6 +235,8 @@ private slots: void importModuleWithLexicallyScopedVars(); void importExportErrors(); + void equality(); + public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -4622,6 +4624,14 @@ void tst_QJSEngine::importExportErrors() } } +void tst_QJSEngine::equality() +{ + QJSEngine engine; + QJSValue ok = engine.evaluate("(0 < 0) ? 'ko' : 'ok'"); + QVERIFY(ok.isString()); + QCOMPARE(ok.toString(), QString("ok")); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3 From 52ac0ea8cbdc9a2b8e895ceee09994fba229ee12 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 8 Mar 2019 10:51:57 +0100 Subject: QUICK_TEST_MAIN_WITH_SETUP: fix qmlEngineAvailable() being called too late When I added the macro, I wasn't aware that TestCaseCollector was a thing. TestCaseCollector loads each QML file without running the tests (i.e. creates a QQmlComponent from the file without creating an object from that component). Since it still executes imports, the test can fail if types are registered or import paths added in qmlEngineAvailable(), since it's called too late. So, call it earlier. This should have no adverse effect on user code, as nothing of importance to the user will be skipped, and the documentation already details what can be expected by the time qmlEngineAvailable() is called. Change-Id: Ibd3a4b728bc87b90f89cc310fddf668c5879ad83 Fixes: QTBUG-74160 Reviewed-by: Ulf Hermann --- src/qmltest/quicktest.cpp | 15 +++++++-------- .../quicktest/quicktestmainwithsetup/data/tst_setup.qml | 6 ++++++ .../imports/ImportPathQmlModule/ImportPathQmlType.qml | 3 +++ .../imports/ImportPathQmlModule/qmldir | 2 ++ .../quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp | 14 ++++++++++++++ 5 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml create mode 100644 tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 1a5cad5d5a..5288bb34f1 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -503,6 +503,13 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch qmlFileSelector->setExtraSelectors(fileSelectors); } + // Do this down here so that import paths, plugin paths, file selectors, etc. are available + // in case the user needs access to them. Do it _before_ the TestCaseCollector parses the + // QML files though, because it attempts to import modules, which might not be available + // if qmlRegisterType()/QQmlEngine::addImportPath() are called in qmlEngineAvailable(). + if (setup) + QMetaObject::invokeMethod(setup, "qmlEngineAvailable", Q_ARG(QQmlEngine*, &engine)); + TestCaseCollector testCaseCollector(fi, &engine); if (!testCaseCollector.errors().isEmpty()) { for (const QQmlError &error : testCaseCollector.errors()) @@ -534,14 +541,6 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch view.rootContext()->setContextProperty (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead - // Do this down here so that import paths, plugin paths, - // file selectors, etc. are available in case the user needs access to them. - if (setup) { - // Don't check the return value; it's OK if it doesn't exist. - // If we add more callbacks in the future, it makes sense if the user only implements one of them. - QMetaObject::invokeMethod(setup, "qmlEngineAvailable", Q_ARG(QQmlEngine*, view.engine())); - } - view.setObjectName(fi.baseName()); view.setTitle(view.objectName()); QTestRootObject::instance()->init(); diff --git a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml index 0f5466998a..ea6b3e014b 100644 --- a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml +++ b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml @@ -29,9 +29,15 @@ import QtQuick 2.0 import QtTest 1.2 +import QmlRegisterTypeCppModule 1.0 +import ImportPathQmlModule 1.0 + TestCase { name: "setup" + QmlRegisterTypeCppType {} + ImportPathQmlType {} + function initTestCase() { verify(qmlEngineAvailableCalled) diff --git a/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml new file mode 100644 index 0000000000..617bdaaf67 --- /dev/null +++ b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/ImportPathQmlType.qml @@ -0,0 +1,3 @@ +import QtQuick 2.0 + +Item {} diff --git a/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir new file mode 100644 index 0000000000..dea7c9a8a4 --- /dev/null +++ b/tests/auto/quicktest/quicktestmainwithsetup/imports/ImportPathQmlModule/qmldir @@ -0,0 +1,2 @@ +module ImportPathQmlModule +ImportPathQmlType 1.0 ImportPathQmlType.qml diff --git a/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp index b0545d1a95..b5deeceac4 100644 --- a/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp +++ b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp @@ -35,6 +35,14 @@ #include "../../shared/util.h" +class QmlRegisterTypeCppType : public QObject +{ + Q_OBJECT + +public: + QmlRegisterTypeCppType() {} +}; + class CustomTestSetup : public QObject { Q_OBJECT @@ -45,6 +53,12 @@ public: public slots: void qmlEngineAvailable(QQmlEngine *qmlEngine) { + // Test that modules are successfully imported by the TestCaseCollector that + // parses the QML files (but doesn't run them). For that to happen, qmlEngineAvailable() + // must be called before TestCaseCollector does its thing. + qmlRegisterType("QmlRegisterTypeCppModule", 1, 0, "QmlRegisterTypeCppType"); + qmlEngine->addImportPath(QString::fromUtf8(QT_QMLTEST_DATADIR) + "/../imports"); + qmlEngine->rootContext()->setContextProperty("qmlEngineAvailableCalled", true); } }; -- cgit v1.2.3 From 2fcbb80e3306d809b708cf46c0f6d0852fe87e17 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 6 Dec 2018 12:47:30 +0100 Subject: Don't warn if invokable test setup function doesn't exist This was never the intention (as the code comments imply), but it was never tested. This is a cherry-pick of 50f234df500829a0023ed5d396c486f995ad71ef because it went to dev (5.13) when it should have originally went to 5.12. Change-Id: I8df0b3702129b1f1d086df73117d3ddb721317cb Reviewed-by: Gatis Paeglis (cherry picked from commit 50f234df500829a0023ed5d396c486f995ad71ef) Reviewed-by: Richard Moe Gustavsen --- src/qmltest/quicktest.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 5288bb34f1..22d329bb80 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -199,6 +199,21 @@ bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000) return spy.size(); } +void maybeInvokeSetupMethod(QObject *setupObject, const char *member, QGenericArgument val0 = QGenericArgument(nullptr)) +{ + // It's OK if it doesn't exist: since we have more than one callback that + // can be called, it makes sense if the user only implements one of them. + // We do this the long way rather than just calling the static + // QMetaObject::invokeMethod(), because that will issue a warning if the + // function doesn't exist, which we don't want. + const QMetaObject *setupMetaObject = setupObject->metaObject(); + const int methodIndex = setupMetaObject->indexOfMethod(member); + if (methodIndex != -1) { + const QMetaMethod method = setupMetaObject->method(methodIndex); + method.invoke(setupObject, val0); + } +} + using namespace QV4::CompiledData; class TestCaseCollector @@ -360,10 +375,8 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch } } - if (setup) { - // Don't check the return value; it's OK if it doesn't exist. - QMetaObject::invokeMethod(setup, "applicationAvailable"); - } + if (setup) + maybeInvokeSetupMethod(setup, "applicationAvailable()"); // Look for QML-specific command-line options. // -import dir Specify an import directory. @@ -508,7 +521,7 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch // QML files though, because it attempts to import modules, which might not be available // if qmlRegisterType()/QQmlEngine::addImportPath() are called in qmlEngineAvailable(). if (setup) - QMetaObject::invokeMethod(setup, "qmlEngineAvailable", Q_ARG(QQmlEngine*, &engine)); + maybeInvokeSetupMethod(setup, "qmlEngineAvailable(QQmlEngine*)", Q_ARG(QQmlEngine*, &engine)); TestCaseCollector testCaseCollector(fi, &engine); if (!testCaseCollector.errors().isEmpty()) { @@ -591,10 +604,8 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch } } - if (setup) { - // Don't check the return value; it's OK if it doesn't exist. - QMetaObject::invokeMethod(setup, "cleanupTestCase"); - } + if (setup) + maybeInvokeSetupMethod(setup, "cleanupTestCase()"); // Flush the current logging stream. QuickTestResult::setProgramName(nullptr); -- cgit v1.2.3 From 470f8fc2b0fe6820d74098807d887284abcf2d62 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 8 Mar 2019 12:09:17 +0100 Subject: Document since versions for QUICK_TEST_MAIN_WITH_SETUP functions - qmlEngineAvailable() 5.11 c260d3062de83d7f051e531007771455915285e5 - applicationAvailable() 5.12 ef06a6ba7bfb5e38b1bef2e21a764ec74479b158 - cleanupTestCase() 5.12 7bf8d4d3772959f06d4dd0168af7774adde29d76 Change-Id: I7b6b699121248c5ad35a7ee2c99eb3f3c77fd01f Reviewed-by: Paul Wicking --- src/qmltest/doc/src/qtquicktest-index.qdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index dfd15b706b..f7c1ca4911 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -150,11 +150,13 @@ \header \li Name \li Purpose + \li Since \row \li void applicationAvailable() \li Called right after the QApplication object was instantiated. Use this function to setup everything that is not related to QML directly. + \li Qt 5.12 \row \li void qmlEngineAvailable(QQmlEngine*) \li Called when the QML engine is available. @@ -162,10 +164,12 @@ \l {QQmlEngine::addPluginPath}{plugin paths}, and \l {QQmlFileSelector::setExtraSelectors}{extra file selectors} will have been set on the engine by this point. + \li Qt 5.11 \row \li void cleanupTestCase() \li Called right after the test execution has finished. Use this function to clean up before everything will start to be destructed. + \li Qt 5.12 \endtable Each function will be called once for each \c tst_*.qml file, so any -- cgit v1.2.3 From 8ed74406c0c2bf9a8d82f95a300013403ae44488 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 8 Mar 2019 12:18:58 +0100 Subject: Doc: improve QUICK_TEST_MAIN_WITH_SETUP function descriptions Change-Id: I83c8a3cb8d125df83deaefdbb08b4271eda8a5b4 Reviewed-by: Paul Wicking --- src/qmltest/doc/src/qtquicktest-index.qdoc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index f7c1ca4911..bee160f6e8 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -154,8 +154,8 @@ \row \li void applicationAvailable() \li Called right after the QApplication object was instantiated. - Use this function to setup everything that is not related - to QML directly. + Use this function to perform setup that does not require a + \l QQmlEngine instance. \li Qt 5.12 \row \li void qmlEngineAvailable(QQmlEngine*) @@ -164,6 +164,11 @@ \l {QQmlEngine::addPluginPath}{plugin paths}, and \l {QQmlFileSelector::setExtraSelectors}{extra file selectors} will have been set on the engine by this point. + + This function can be used to \l {Choosing the Correct Integration + Method Between C++ and QML}{register QML types} and + \l {QQmlEngine::addImportPath()}{add import paths}, + amongst other things. \li Qt 5.11 \row \li void cleanupTestCase() -- cgit v1.2.3 From d47f0ce2cb6d6fb2c0d0a449d17d8c1dacbbcbe4 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 19 Mar 2019 09:36:07 +0100 Subject: Blacklist tst_QQuickMultiPointTouchArea::nested on opensuse Task-number: QTBUG-59960 Change-Id: I4a4d607c84600c83475d0ea5c10c9466d4dc626c Reviewed-by: Mitch Curtis --- tests/auto/quick/qquickmultipointtoucharea/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST index cdb3e7733b..0be39b3bf1 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST +++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST @@ -4,3 +4,5 @@ ubuntu-18.04 [nested] ubuntu-16.04 ubuntu-18.04 +opensuse-42.3 +opensuse-leap -- cgit v1.2.3 From 4d80e1c6a9d04d87b3ddcb3e1724281525a27442 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 1 Mar 2019 09:58:04 +0100 Subject: Accelerate lookups of properties in outer QML contexts If we can determine with certainty that a property cannot be found in the innermost QML context, then we can avoid one entire iteration for all future lookups. Task-number: QTBUG-69898 Change-Id: I2a579aa9f60811a818e45235a60a93fc2ede3206 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4qmlcontext.cpp | 165 ++++++++++++++++++++++++------------ src/qml/jsruntime/qv4qmlcontext_p.h | 1 + 2 files changed, 114 insertions(+), 52 deletions(-) diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 97b955632d..a2c8e3916f 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -79,6 +79,53 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } +static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, QQmlContextData *context, String *name, + bool *hasProperty, Value *base, QV4::Lookup *lookup, + QV4::Lookup *originalLookup, QQmlEnginePrivate *ep) +{ + const QV4::IdentifierHash &properties = context->propertyNames(); + if (properties.count() == 0) + return OptionalReturnedValue(); + + const int propertyIdx = properties.value(name); + + if (propertyIdx == -1) + return OptionalReturnedValue(); + + if (propertyIdx < context->idValueCount) { + if (hasProperty) + *hasProperty = true; + + if (lookup) { + lookup->qmlContextIdObjectLookup.objectId = propertyIdx; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject; + return OptionalReturnedValue(lookup->qmlContextPropertyGetter(lookup, v4, base)); + } else if (originalLookup) { + originalLookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInParentContextHierarchy; + } + + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); + return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx])); + } + + QQmlContextPrivate *cp = context->asQQmlContextPrivate(); + + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); + + const QVariant &value = cp->propertyValues.at(propertyIdx); + if (hasProperty) + *hasProperty = true; + if (value.userType() == qMetaTypeId >()) { + QQmlListProperty prop(context->asQQmlContext(), (void*) qintptr(propertyIdx), + QQmlContextPrivate::context_count, + QQmlContextPrivate::context_at); + return OptionalReturnedValue(QmlListWrapper::create(v4, prop, qMetaTypeId >())); + } + return OptionalReturnedValue(v4->fromVariant(cp->propertyValues.at(propertyIdx))); +} + ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup) { if (!id.isString()) @@ -224,47 +271,8 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } while (context) { - // Search context properties - const QV4::IdentifierHash &properties = context->propertyNames(); - if (properties.count()) { - int propertyIdx = properties.value(name); - - if (propertyIdx != -1) { - - if (propertyIdx < context->idValueCount) { - if (hasProperty) - *hasProperty = true; - - if (lookup) { - lookup->qmlContextIdObjectLookup.objectId = propertyIdx; - lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject; - return lookup->qmlContextPropertyGetter(lookup, v4, base); - } - - if (ep->propertyCapture) - ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); - return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]); - } else { - - QQmlContextPrivate *cp = context->asQQmlContextPrivate(); - - if (ep->propertyCapture) - ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); - - const QVariant &value = cp->propertyValues.at(propertyIdx); - if (hasProperty) - *hasProperty = true; - if (value.userType() == qMetaTypeId >()) { - QQmlListProperty prop(context->asQQmlContext(), (void*) qintptr(propertyIdx), - QQmlContextPrivate::context_count, - QQmlContextPrivate::context_at); - return QmlListWrapper::create(v4, prop, qMetaTypeId >()); - } else { - return scope.engine->fromVariant(cp->propertyValues.at(propertyIdx)); - } - } - } - } + if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep)) + return *property; // Search scope object if (scopeObject) { @@ -311,17 +319,21 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r if (base) *base = QV4::QObjectWrapper::wrap(v4, context->contextObject); - if (lookup && propertyData) { - QQmlData *ddata = QQmlData::get(context->contextObject, false); - if (ddata && ddata->propertyCache) { - ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); - const QObjectWrapper *That = static_cast(val->objectValue()); - lookup->qobjectLookup.ic = That->internalClass(); - lookup->qobjectLookup.staticQObject = nullptr; - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = propertyData; - lookup->qmlContextPropertyGetter = contextGetterFunction; + if (propertyData) { + if (lookup) { + QQmlData *ddata = QQmlData::get(context->contextObject, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); + const QObjectWrapper *That = static_cast(val->objectValue()); + lookup->qobjectLookup.ic = That->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = propertyData; + lookup->qmlContextPropertyGetter = contextGetterFunction; + } + } else if (originalLookup) { + originalLookup->qmlContextPropertyGetter = lookupInParentContextHierarchy; } } @@ -576,6 +588,55 @@ ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngin return result; } +ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Scope scope(engine); + Scoped qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::undefined(); + + QQmlContextData *expressionContext = context; + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); + + PropertyKey id =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[l->nameIndex]); + ScopedString name(scope, id.asStringOrSymbol()); + + ScopedValue result(scope); + + for (context = context->parent; context; context = context->parent) { + if (auto property = searchContextProperties(engine, context, name, nullptr, base, nullptr, nullptr, ep)) + return *property; + + // Search context object + if (context->contextObject) { + bool hasProp = false; + result = QV4::QObjectWrapper::getQmlProperty(engine, context, context->contextObject, + name, QV4::QObjectWrapper::CheckRevision, &hasProp); + if (hasProp) { + if (base) + *base = QV4::QObjectWrapper::wrap(engine, context->contextObject); + + return result->asReturnedValue(); + } + } + } + + bool hasProp = false; + result = engine->globalObject->get(name, &hasProp); + if (hasProp) + return result->asReturnedValue(); + + expressionContext->unresolvedNames = true; + + return Encode::undefined(); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 6375294375..4c8287ef2f 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -111,6 +111,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext -- cgit v1.2.3 From 9ce826901bffd3b63d23a61babdc667c3f8ec567 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 26 Feb 2019 09:47:34 +0100 Subject: Blacklist tst_QQuickMultiPointTouchArea::nonOverlapping for openSuse The test is flaky, just like it is on Ubuntu. Task-number: QTBUG-74072 Change-Id: I65b1f7a00a72abd57460957a6399a86c213d082b Reviewed-by: Mitch Curtis --- tests/auto/quick/qquickmultipointtoucharea/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST index 0be39b3bf1..de939b5273 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST +++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST @@ -1,6 +1,8 @@ [nonOverlapping] ubuntu-16.04 ubuntu-18.04 +opensuse-42.3 +opensuse-leap [nested] ubuntu-16.04 ubuntu-18.04 -- cgit v1.2.3 From 497a795081b95d487f0ec33746cc2a12ffeae5a0 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 21 Mar 2019 16:25:55 +0100 Subject: Initialize TranslationData padding before writing cache files Otherwise the resulting files differ subtly. Fixes: QTBUG-74532 Change-Id: I12b4f1ba6dda781d63ad50cce87861ba24582bf7 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 2 + tests/auto/qml/qmlcachegen/Enums.qml | 9 --- tests/auto/qml/qmlcachegen/data/Enums.qml | 9 +++ .../auto/qml/qmlcachegen/data/componentInItem.qml | 15 ++++ tests/auto/qml/qmlcachegen/data/jsimport.qml | 6 ++ tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml | 6 ++ tests/auto/qml/qmlcachegen/data/library.js | 4 + tests/auto/qml/qmlcachegen/data/script.js | 6 ++ tests/auto/qml/qmlcachegen/data/script.mjs | 4 + tests/auto/qml/qmlcachegen/data/trickypaths.qml | 4 + tests/auto/qml/qmlcachegen/data/umlaut.qml | 4 + .../data/versionStyleSuffix-1.2-core-yc.qml | 4 + .../data/versionStyleSuffix-1.2-more.qml | 4 + tests/auto/qml/qmlcachegen/data/versionchecks.qml | 4 + tests/auto/qml/qmlcachegen/data/worker.js | 3 + tests/auto/qml/qmlcachegen/data/worker.qml | 12 +++ tests/auto/qml/qmlcachegen/jsimport.qml | 6 -- tests/auto/qml/qmlcachegen/jsmoduleimport.qml | 6 -- tests/auto/qml/qmlcachegen/library.js | 4 - tests/auto/qml/qmlcachegen/qmlcachegen.pro | 30 +++++--- tests/auto/qml/qmlcachegen/script.js | 6 -- tests/auto/qml/qmlcachegen/script.mjs | 4 - tests/auto/qml/qmlcachegen/trickypaths.qml | 4 - tests/auto/qml/qmlcachegen/trickypaths.qrc | 6 +- tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc | 2 +- tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 86 ++++++++++++++++------ tests/auto/qml/qmlcachegen/umlaut.qml | 4 - .../qmlcachegen/versionStyleSuffix-1.2-core-yc.qml | 4 - .../qmlcachegen/versionStyleSuffix-1.2-more.qml | 4 - tests/auto/qml/qmlcachegen/versionchecks.qml | 4 - tests/auto/qml/qmlcachegen/worker.js | 3 - tests/auto/qml/qmlcachegen/worker.qml | 12 --- 32 files changed, 175 insertions(+), 106 deletions(-) delete mode 100644 tests/auto/qml/qmlcachegen/Enums.qml create mode 100644 tests/auto/qml/qmlcachegen/data/Enums.qml create mode 100644 tests/auto/qml/qmlcachegen/data/componentInItem.qml create mode 100644 tests/auto/qml/qmlcachegen/data/jsimport.qml create mode 100644 tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml create mode 100644 tests/auto/qml/qmlcachegen/data/library.js create mode 100644 tests/auto/qml/qmlcachegen/data/script.js create mode 100644 tests/auto/qml/qmlcachegen/data/script.mjs create mode 100644 tests/auto/qml/qmlcachegen/data/trickypaths.qml create mode 100644 tests/auto/qml/qmlcachegen/data/umlaut.qml create mode 100644 tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml create mode 100644 tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml create mode 100644 tests/auto/qml/qmlcachegen/data/versionchecks.qml create mode 100644 tests/auto/qml/qmlcachegen/data/worker.js create mode 100644 tests/auto/qml/qmlcachegen/data/worker.qml delete mode 100644 tests/auto/qml/qmlcachegen/jsimport.qml delete mode 100644 tests/auto/qml/qmlcachegen/jsmoduleimport.qml delete mode 100644 tests/auto/qml/qmlcachegen/library.js delete mode 100644 tests/auto/qml/qmlcachegen/script.js delete mode 100644 tests/auto/qml/qmlcachegen/script.mjs delete mode 100644 tests/auto/qml/qmlcachegen/trickypaths.qml delete mode 100644 tests/auto/qml/qmlcachegen/umlaut.qml delete mode 100644 tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml delete mode 100644 tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml delete mode 100644 tests/auto/qml/qmlcachegen/versionchecks.qml delete mode 100644 tests/auto/qml/qmlcachegen/worker.js delete mode 100644 tests/auto/qml/qmlcachegen/worker.qml diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index ab43ea350b..868f600a10 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1108,6 +1108,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg QV4::CompiledData::TranslationData translationData; translationData.number = -1; translationData.commentIndex = 0; // empty string + translationData.padding = 0; if (!args || !args->expression) return; // no arguments, stop @@ -1148,6 +1149,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg QV4::CompiledData::TranslationData translationData; translationData.number = -1; translationData.commentIndex = 0; // empty string, but unused + translationData.padding = 0; if (!args || !args->expression) return; // no arguments, stop diff --git a/tests/auto/qml/qmlcachegen/Enums.qml b/tests/auto/qml/qmlcachegen/Enums.qml deleted file mode 100644 index 830babb73e..0000000000 --- a/tests/auto/qml/qmlcachegen/Enums.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQml 2.0 -QtObject { - enum Test { - First = 100, - Second = 200 - } - property int value: 0 - Component.onCompleted: value = Enums.Second -} diff --git a/tests/auto/qml/qmlcachegen/data/Enums.qml b/tests/auto/qml/qmlcachegen/data/Enums.qml new file mode 100644 index 0000000000..830babb73e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/Enums.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 +QtObject { + enum Test { + First = 100, + Second = 200 + } + property int value: 0 + Component.onCompleted: value = Enums.Second +} diff --git a/tests/auto/qml/qmlcachegen/data/componentInItem.qml b/tests/auto/qml/qmlcachegen/data/componentInItem.qml new file mode 100644 index 0000000000..820b9fddcd --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/componentInItem.qml @@ -0,0 +1,15 @@ +import QtQuick 2.12 + +Item { + Component { + Rectangle { + id: xxx + Text { + text: qsTr("&Undo") + } + Text { + text: qsTr("&Redo") + } + } + } +} diff --git a/tests/auto/qml/qmlcachegen/data/jsimport.qml b/tests/auto/qml/qmlcachegen/data/jsimport.qml new file mode 100644 index 0000000000..9c40878e60 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/jsimport.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "script.js" as Script + +QtObject { + property int value: Script.getter() +} diff --git a/tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml b/tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml new file mode 100644 index 0000000000..c1fad7fee2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/jsmoduleimport.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import "script.mjs" as Script + +QtObject { + property bool ok: Script.ok() +} diff --git a/tests/auto/qml/qmlcachegen/data/library.js b/tests/auto/qml/qmlcachegen/data/library.js new file mode 100644 index 0000000000..51fb41dc23 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/library.js @@ -0,0 +1,4 @@ + +function getter() { + return 42; +} diff --git a/tests/auto/qml/qmlcachegen/data/script.js b/tests/auto/qml/qmlcachegen/data/script.js new file mode 100644 index 0000000000..fa55f9069e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/script.js @@ -0,0 +1,6 @@ + +.import "library.js" as Library + +function getter() { + return Library.getter() +} diff --git a/tests/auto/qml/qmlcachegen/data/script.mjs b/tests/auto/qml/qmlcachegen/data/script.mjs new file mode 100644 index 0000000000..459c336125 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/script.mjs @@ -0,0 +1,4 @@ + +export function ok() { + return true +} diff --git a/tests/auto/qml/qmlcachegen/data/trickypaths.qml b/tests/auto/qml/qmlcachegen/data/trickypaths.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/trickypaths.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/data/umlaut.qml b/tests/auto/qml/qmlcachegen/data/umlaut.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/umlaut.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-core-yc.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/versionStyleSuffix-1.2-more.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/data/versionchecks.qml b/tests/auto/qml/qmlcachegen/data/versionchecks.qml new file mode 100644 index 0000000000..77d67e7da4 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/versionchecks.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property bool ok: true +} diff --git a/tests/auto/qml/qmlcachegen/data/worker.js b/tests/auto/qml/qmlcachegen/data/worker.js new file mode 100644 index 0000000000..dd2d0b843d --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/worker.js @@ -0,0 +1,3 @@ +WorkerScript.onMessage = function(message) { + WorkerScript.sendMessage({ reply: message }); +} diff --git a/tests/auto/qml/qmlcachegen/data/worker.qml b/tests/auto/qml/qmlcachegen/data/worker.qml new file mode 100644 index 0000000000..1f1c9d1ac7 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/data/worker.qml @@ -0,0 +1,12 @@ +import QtQml 2.0 +import QtQuick 2.0 +QtObject { + property bool success: false + property WorkerScript worker: WorkerScript { + source: "worker.js" + onMessage: { + success = true + } + } + Component.onCompleted: worker.sendMessage("Hello") +} diff --git a/tests/auto/qml/qmlcachegen/jsimport.qml b/tests/auto/qml/qmlcachegen/jsimport.qml deleted file mode 100644 index 9c40878e60..0000000000 --- a/tests/auto/qml/qmlcachegen/jsimport.qml +++ /dev/null @@ -1,6 +0,0 @@ -import QtQml 2.0 -import "script.js" as Script - -QtObject { - property int value: Script.getter() -} diff --git a/tests/auto/qml/qmlcachegen/jsmoduleimport.qml b/tests/auto/qml/qmlcachegen/jsmoduleimport.qml deleted file mode 100644 index c1fad7fee2..0000000000 --- a/tests/auto/qml/qmlcachegen/jsmoduleimport.qml +++ /dev/null @@ -1,6 +0,0 @@ -import QtQml 2.0 -import "script.mjs" as Script - -QtObject { - property bool ok: Script.ok() -} diff --git a/tests/auto/qml/qmlcachegen/library.js b/tests/auto/qml/qmlcachegen/library.js deleted file mode 100644 index 51fb41dc23..0000000000 --- a/tests/auto/qml/qmlcachegen/library.js +++ /dev/null @@ -1,4 +0,0 @@ - -function getter() { - return 42; -} diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index 7f7b3128cf..7f8e93d101 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -2,23 +2,31 @@ CONFIG += testcase qtquickcompiler TARGET = tst_qmlcachegen macos:CONFIG -= app_bundle +include (../../shared/util.pri) +TESTDATA = data/* + SOURCES += tst_qmlcachegen.cpp -workerscripts_test.files = worker.js worker.qml +RESOURCES += \ + data/versionchecks.qml \ + data/jsimport.qml \ + data/script.js \ + data/library.js \ + data/Enums.qml \ + data/componentInItem.qml \ + data/jsmoduleimport.qml \ + data/script.mjs + +workerscripts_test.files = \ + data/worker.js \ + data/worker.qml workerscripts_test.prefix = /workerscripts -RESOURCES += workerscripts_test - -RESOURCES += versionchecks.qml - -RESOURCES += trickypaths.qrc -RESOURCES += jsimport.qml script.js library.js - -RESOURCES += Enums.qml +RESOURCES += \ + workerscripts_test \ + trickypaths.qrc # QTBUG-46375 !win32: RESOURCES += trickypaths_umlaut.qrc -RESOURCES += jsmoduleimport.qml script.mjs - QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmlcachegen/script.js b/tests/auto/qml/qmlcachegen/script.js deleted file mode 100644 index fa55f9069e..0000000000 --- a/tests/auto/qml/qmlcachegen/script.js +++ /dev/null @@ -1,6 +0,0 @@ - -.import "library.js" as Library - -function getter() { - return Library.getter() -} diff --git a/tests/auto/qml/qmlcachegen/script.mjs b/tests/auto/qml/qmlcachegen/script.mjs deleted file mode 100644 index 459c336125..0000000000 --- a/tests/auto/qml/qmlcachegen/script.mjs +++ /dev/null @@ -1,4 +0,0 @@ - -export function ok() { - return true -} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qml b/tests/auto/qml/qmlcachegen/trickypaths.qml deleted file mode 100644 index 0836808dc2..0000000000 --- a/tests/auto/qml/qmlcachegen/trickypaths.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQml 2.0 -QtObject { - property int success: 42 -} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qrc b/tests/auto/qml/qmlcachegen/trickypaths.qrc index 57977ccf6d..b0c3bcf209 100644 --- a/tests/auto/qml/qmlcachegen/trickypaths.qrc +++ b/tests/auto/qml/qmlcachegen/trickypaths.qrc @@ -1,7 +1,7 @@ -trickypaths.qml -versionStyleSuffix-1.2-core-yc.qml -versionStyleSuffix-1.2-more.qml +data/trickypaths.qml +data/versionStyleSuffix-1.2-core-yc.qml +data/versionStyleSuffix-1.2-more.qml diff --git a/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc index 9ca889d692..17aa30473f 100644 --- a/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc +++ b/tests/auto/qml/qmlcachegen/trickypaths_umlaut.qrc @@ -1,5 +1,5 @@ -umlaut.qml +data/umlaut.qml diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 97ac466e94..e290f21986 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -38,7 +38,9 @@ #include #include -class tst_qmlcachegen: public QObject +#include "../../shared/util.h" + +class tst_qmlcachegen: public QQmlDataTest { Q_OBJECT @@ -65,6 +67,9 @@ private slots: void enums(); void sourceFileIndices(); + + void reproducibleCache_data(); + void reproducibleCache(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -352,12 +357,13 @@ static QQmlPrivate::CachedQmlUnit *temporaryModifiedCachedUnit = nullptr; void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() { - QVERIFY(QFile::exists(":/versionchecks.qml")); - QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + QVERIFY(QFile::exists(":/data/versionchecks.qml")); + QCOMPARE(QFileInfo(":/data/versionchecks.qml").size(), 0); Q_ASSERT(!temporaryModifiedCachedUnit); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; - const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit( + QUrl("qrc:/data/versionchecks.qml"), &error); QVERIFY(originalUnit); QV4::CompiledData::Unit *tweakedUnit = (QV4::CompiledData::Unit *)malloc(originalUnit->unitSize); memcpy(reinterpret_cast(tweakedUnit), reinterpret_cast(originalUnit), originalUnit->unitSize); @@ -365,7 +371,7 @@ void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() temporaryModifiedCachedUnit = new QQmlPrivate::CachedQmlUnit{tweakedUnit, nullptr, nullptr}; auto testHandler = [](const QUrl &url) -> const QQmlPrivate::CachedQmlUnit * { - if (url == QUrl("qrc:/versionchecks.qml")) + if (url == QUrl("qrc:/data/versionchecks.qml")) return temporaryModifiedCachedUnit; return nullptr; }; @@ -373,15 +379,18 @@ void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() { QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; - QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error)); + QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/data/versionchecks.qml"), &error)); QCOMPARE(error, QQmlMetaType::CachedUnitLookupError::VersionMismatch); } { QQmlEngine engine; - QQmlComponent component(&engine, QUrl("qrc:/versionchecks.qml")); + QQmlComponent component(&engine, QUrl("qrc:/data/versionchecks.qml")); QCOMPARE(component.status(), QQmlComponent::Error); - QCOMPARE(component.errorString(), QString("qrc:/versionchecks.qml:-1 File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile\n")); + QCOMPARE(component.errorString(), + QString("qrc:/data/versionchecks.qml:-1 File was compiled ahead of time with an " + "incompatible version of Qt and the original file cannot be found. Please " + "recompile\n")); } Q_ASSERT(temporaryModifiedCachedUnit); @@ -394,12 +403,12 @@ void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() void tst_qmlcachegen::workerScripts() { - QVERIFY(QFile::exists(":/workerscripts/worker.js")); - QVERIFY(QFile::exists(":/workerscripts/worker.qml")); - QCOMPARE(QFileInfo(":/workerscripts/worker.js").size(), 0); + QVERIFY(QFile::exists(":/workerscripts/data/worker.js")); + QVERIFY(QFile::exists(":/workerscripts/data/worker.qml")); + QCOMPARE(QFileInfo(":/workerscripts/data/worker.js").size(), 0); QQmlEngine engine; - CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/worker.qml")); + CleanlyLoadingComponent component(&engine, QUrl("qrc:///workerscripts/data/worker.qml")); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QTRY_VERIFY(obj->property("success").toBool()); @@ -496,7 +505,7 @@ void tst_qmlcachegen::trickyPaths() void tst_qmlcachegen::qrcScriptImport() { QQmlEngine engine; - CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsimport.qml")); + CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/jsimport.qml")); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QTRY_COMPARE(obj->property("value").toInt(), 42); @@ -559,14 +568,14 @@ void tst_qmlcachegen::fsScriptImport() void tst_qmlcachegen::moduleScriptImport() { QQmlEngine engine; - CleanlyLoadingComponent component(&engine, QUrl("qrc:///jsmoduleimport.qml")); + CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/jsmoduleimport.qml")); QVERIFY2(!component.isError(), qPrintable(component.errorString())); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QTRY_VERIFY(obj->property("ok").toBool()); - QVERIFY(QFile::exists(":/script.mjs")); - QCOMPARE(QFileInfo(":/script.mjs").size(), 0); + QVERIFY(QFile::exists(":/data/script.mjs")); + QCOMPARE(QFileInfo(":/data/script.mjs").size(), 0); { auto componentPrivate = QQmlComponentPrivate::get(&component); @@ -579,7 +588,8 @@ void tst_qmlcachegen::moduleScriptImport() QVERIFY(unitData->flags & QV4::CompiledData::Unit::IsESModule); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; - const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/script.mjs"), &error); + const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit( + QUrl("qrc:/data/script.mjs"), &error); QVERIFY(unitFromResources); QCOMPARE(unitFromResources, compilationUnit->unitData()); @@ -589,7 +599,7 @@ void tst_qmlcachegen::moduleScriptImport() void tst_qmlcachegen::enums() { QQmlEngine engine; - CleanlyLoadingComponent component(&engine, QUrl("qrc:///Enums.qml")); + CleanlyLoadingComponent component(&engine, QUrl("qrc:///data/Enums.qml")); QScopedPointer obj(component.create()); QVERIFY(!obj.isNull()); QTRY_COMPARE(obj->property("value").toInt(), 200); @@ -597,16 +607,50 @@ void tst_qmlcachegen::enums() void tst_qmlcachegen::sourceFileIndices() { - QVERIFY(QFile::exists(":/versionchecks.qml")); - QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + QVERIFY(QFile::exists(":/data/versionchecks.qml")); + QCOMPARE(QFileInfo(":/data/versionchecks.qml").size(), 0); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; - const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + const QV4::CompiledData::Unit *unitFromResources = QQmlMetaType::findCachedCompilationUnit( + QUrl("qrc:/data/versionchecks.qml"), &error); QVERIFY(unitFromResources); QVERIFY(unitFromResources->flags & QV4::CompiledData::Unit::PendingTypeCompilation); QCOMPARE(uint(unitFromResources->sourceFileIndex), uint(0)); } +void tst_qmlcachegen::reproducibleCache_data() +{ + QTest::addColumn("filePath"); + + QDir dir(dataDirectory()); + for (const QString &entry : dir.entryList(QDir::Files)) { + QVERIFY(entry.endsWith(".qml") || entry.endsWith(".js") || entry.endsWith(".mjs")); + QTest::newRow(entry.toUtf8().constData()) << dir.filePath(entry); + } +} + +void tst_qmlcachegen::reproducibleCache() +{ + QFETCH(QString, filePath); + + QFile file(filePath); + QVERIFY(file.exists()); + + auto generate = [](const QString &path) { + if (!generateCache(path)) + return QByteArray(); + QFile generated(path + 'c'); + [&](){ QVERIFY(generated.open(QIODevice::ReadOnly)); }(); + const QByteArray result = generated.readAll(); + generated.remove(); + return result; + }; + + const QByteArray contents1 = generate(file.fileName()); + const QByteArray contents2 = generate(file.fileName()); + QCOMPARE(contents1, contents2); +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" diff --git a/tests/auto/qml/qmlcachegen/umlaut.qml b/tests/auto/qml/qmlcachegen/umlaut.qml deleted file mode 100644 index 0836808dc2..0000000000 --- a/tests/auto/qml/qmlcachegen/umlaut.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQml 2.0 -QtObject { - property int success: 42 -} diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml deleted file mode 100644 index 0836808dc2..0000000000 --- a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-core-yc.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQml 2.0 -QtObject { - property int success: 42 -} diff --git a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml b/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml deleted file mode 100644 index 0836808dc2..0000000000 --- a/tests/auto/qml/qmlcachegen/versionStyleSuffix-1.2-more.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQml 2.0 -QtObject { - property int success: 42 -} diff --git a/tests/auto/qml/qmlcachegen/versionchecks.qml b/tests/auto/qml/qmlcachegen/versionchecks.qml deleted file mode 100644 index 77d67e7da4..0000000000 --- a/tests/auto/qml/qmlcachegen/versionchecks.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQml 2.0 -QtObject { - property bool ok: true -} diff --git a/tests/auto/qml/qmlcachegen/worker.js b/tests/auto/qml/qmlcachegen/worker.js deleted file mode 100644 index dd2d0b843d..0000000000 --- a/tests/auto/qml/qmlcachegen/worker.js +++ /dev/null @@ -1,3 +0,0 @@ -WorkerScript.onMessage = function(message) { - WorkerScript.sendMessage({ reply: message }); -} diff --git a/tests/auto/qml/qmlcachegen/worker.qml b/tests/auto/qml/qmlcachegen/worker.qml deleted file mode 100644 index 1f1c9d1ac7..0000000000 --- a/tests/auto/qml/qmlcachegen/worker.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQml 2.0 -import QtQuick 2.0 -QtObject { - property bool success: false - property WorkerScript worker: WorkerScript { - source: "worker.js" - onMessage: { - success = true - } - } - Component.onCompleted: worker.sendMessage("Hello") -} -- cgit v1.2.3 From a5b4a8dbaa2f6535fa659fb95b75c059a04c6235 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 15 Mar 2019 11:11:45 +0100 Subject: Fix some memory leaks Use QScopedPointer where appropriate to avoid reporting false memory leaks with ASAN's leak checker. Change-Id: Ia3dfeafc66c2ad2ac8454861acce82b99678e517 Reviewed-by: Shawn Rutledge --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index a862604fc1..4cf7fa7119 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -1645,11 +1645,11 @@ void tst_qquickwindow::focusReason() window->setTitle(QTest::currentTestFunction()); QVERIFY(QTest::qWaitForWindowExposed(window)); - QQuickItem *firstItem = new QQuickItem; + QScopedPointer firstItem(new QQuickItem); firstItem->setSize(QSizeF(100, 100)); firstItem->setParentItem(window->contentItem()); - QQuickItem *secondItem = new QQuickItem; + QScopedPointer secondItem(new QQuickItem); secondItem->setSize(QSizeF(100, 100)); secondItem->setParentItem(window->contentItem()); @@ -1673,7 +1673,7 @@ void tst_qquickwindow::ignoreUnhandledMouseEvents() window->show(); QVERIFY(QTest::qWaitForWindowExposed(window)); - QQuickItem *item = new QQuickItem; + QScopedPointer item(new QQuickItem); item->setSize(QSizeF(100, 100)); item->setParentItem(window->contentItem()); @@ -1883,8 +1883,8 @@ void tst_qquickwindow::hideThenDelete() QFETCH(bool, persistentSG); QFETCH(bool, persistentGL); - QSignalSpy *openglDestroyed = nullptr; - QSignalSpy *sgInvalidated = nullptr; + QScopedPointer openglDestroyed; + QScopedPointer sgInvalidated; { QQuickWindow window; @@ -1903,10 +1903,10 @@ void tst_qquickwindow::hideThenDelete() const bool isGL = window.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL; #if QT_CONFIG(opengl) if (isGL) - openglDestroyed = new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed())); + openglDestroyed.reset(new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed()))); #endif - sgInvalidated = new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated())); + sgInvalidated.reset(new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated()))); window.hide(); @@ -1951,7 +1951,7 @@ void tst_qquickwindow::showHideAnimate() QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("showHideAnimate.qml")); - QQuickItem* created = qobject_cast(component.create()); + QScopedPointer created(qobject_cast(component.create())); QVERIFY(created); @@ -2293,7 +2293,7 @@ void tst_qquickwindow::contentItemSize() QQmlEngine engine; QQmlComponent component(&engine); component.setData(QByteArray("import QtQuick 2.1\n Rectangle { anchors.fill: parent }"), QUrl()); - QQuickItem *rect = qobject_cast(component.create()); + QScopedPointer rect(qobject_cast(component.create())); QVERIFY(rect); rect->setParentItem(window.contentItem()); QCOMPARE(QSizeF(rect->width(), rect->height()), size); -- cgit v1.2.3 From 93ed9213f74dcd3806b0b2bfabc9900a545a5c69 Mon Sep 17 00:00:00 2001 From: Jani Heikkinen Date: Thu, 7 Feb 2019 15:04:24 +0200 Subject: Update tests/auto/qml/ecmascripttests/test262 Task-number: QTBUG-73454 Task-number: QTBUG-71209 Change-Id: I12925ce49cc18f4bb6908a5515fc476b32a222dc Reviewed-by: Simon Hausmann (cherry picked from commit 896d49a4e1113e3eb4832b83920b0dfd76987259) --- tests/auto/qml/ecmascripttests/test262 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262 index 3c69133cc4..6b0c42c63c 160000 --- a/tests/auto/qml/ecmascripttests/test262 +++ b/tests/auto/qml/ecmascripttests/test262 @@ -1 +1 @@ -Subproject commit 3c69133cc419840c1be34638039cd8c48a7ef581 +Subproject commit 6b0c42c63c2492bd0a7a96d3179d122b5f71793f -- cgit v1.2.3 From 90a08b0c52fbe20ea3324f7315c04c48c95a8e61 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Mar 2019 09:43:16 +0100 Subject: Fix memory "leaks" in qmlcachegen The CompilationUnit class owns the unit data. An exception was made in bootstrap builds, where it was up to the caller to free the memory. This lead to cases in qmlcachegen where we didn't free the memory. This is best fixed by unifying the behavior. This fixes the build when using an ASAN enabled build, as the runtime aborts after calling qmlcachegen due to "leaks". Change-Id: I8b55b4e302a9569a1d4e09eeb488c479368b50f0 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 10 +++++++++- src/qml/compiler/qv4compileddata_p.h | 4 ---- tools/qmlcachegen/qmlcachegen.cpp | 4 ---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 7906b3572c..9ffcb81fa2 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -98,18 +98,25 @@ CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, setUnitData(unitData, nullptr, fileName, finalUrlString); } -#ifndef V4_BOOTSTRAP CompilationUnit::~CompilationUnit() { +#ifndef V4_BOOTSTRAP unlink(); +#endif if (data) { if (data->qmlUnit() != qmlData) free(const_cast(qmlData)); qmlData = nullptr; +#ifndef V4_BOOTSTRAP if (!(data->flags & QV4::CompiledData::Unit::StaticData)) free(const_cast(data)); +#else + // Unconditionally free the memory. In the dev tools we create units that have + // the flag set and will be saved to disk, so intended to persist later. + free(const_cast(data)); +#endif } data = nullptr; #if Q_BYTE_ORDER == Q_BIG_ENDIAN @@ -120,6 +127,7 @@ CompilationUnit::~CompilationUnit() delete [] imports; imports = nullptr; } +#ifndef V4_BOOTSTRAP QString CompilationUnit::localCacheFilePath(const QUrl &url) { diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 1341c91e97..3fd9fdf74b 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -1079,11 +1079,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase const QmlUnit *qmlData = nullptr; public: CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString()); -#ifdef V4_BOOTSTRAP - ~CompilationUnit() {} -#else ~CompilationUnit(); -#endif void addref() { diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index e8583996bf..2b84a6babe 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -232,8 +232,6 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti if (!saveFunction(irDocument.javaScriptCompilationUnit, &error->message)) return false; - - free(unit); } return true; } @@ -241,7 +239,6 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti static bool compileJSFile(const QString &inputFileName, const QString &inputFileUrl, SaveFunction saveFunction, Error *error) { QQmlRefPointer unit; - QScopedPointer unitDataToFree; QString sourceCode; { @@ -323,7 +320,6 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile generator.generate(irDocument); QV4::CompiledData::Unit *unitData = const_cast(irDocument.javaScriptCompilationUnit->data); unitData->flags |= QV4::CompiledData::Unit::StaticData; - unitDataToFree.reset(unitData); unit = irDocument.javaScriptCompilationUnit; } } -- cgit v1.2.3 From 78888f485f1d70e98243deb2b6ab8911a7f8c9b2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Mar 2019 09:46:24 +0100 Subject: Enable lookups in QML files when compiling ahead of time This should have been enabled in the series earlier when for QTBUG-69898. Change-Id: Ide7507d5dcf439463c22b631d49d654624737d1f Reviewed-by: Lars Knoll --- tools/qmlcachegen/qmlcachegen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 2b84a6babe..d6fae446a1 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -200,7 +200,6 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti &irDocument.jsGenerator, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, &irDocument.jsGenerator.stringTable, illegalNames); - v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode for (QmlIR::Object *object: qAsConst(irDocument.objects)) { if (object->functionsAndExpressions->count == 0) continue; -- cgit v1.2.3 From 2a8fcbbcb3b441d2b5b32740bc828c7bc9096e4c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Mar 2019 14:20:49 +0100 Subject: Try to stabilize qquickrectangle test Similar to commit 9921180a44d9097b1b83c2fa7b3f36dbb555140c, introduce liberal use of QTRY_VERIFY. Fixes: QTBUG-74605 Change-Id: I4552b1df4021f78cddff08f0b7d8629fafe25acb Reviewed-by: Ulf Hermann --- tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp index b34612ee88..832b973d96 100644 --- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp +++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp @@ -159,17 +159,15 @@ void tst_qquickrectangle::gradient_multiple() // Start off clean QQuickItemPrivate *firstRectPriv = QQuickItemPrivate::get(firstRect); QQuickItemPrivate *secondRectPriv = QQuickItemPrivate::get(secondRect); - bool firstIsDirty = firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content; + QTRY_VERIFY(!(firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content)); bool secondIsDirty = secondRectPriv->dirtyAttributes & QQuickItemPrivate::Content; - QVERIFY(!firstIsDirty); QVERIFY(!secondIsDirty); QMetaObject::invokeMethod(view.rootObject(), "changeGradient"); // Changing the gradient should have scheduled an update of both items - firstIsDirty = firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content; + QTRY_VERIFY(firstRectPriv->dirtyAttributes & QQuickItemPrivate::Content); secondIsDirty = secondRectPriv->dirtyAttributes & QQuickItemPrivate::Content; - QVERIFY(firstIsDirty); QVERIFY(secondIsDirty); } -- cgit v1.2.3 From 42ed40cf0abd0155d6e6d1ef01faf9bded065588 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 6 Feb 2019 15:27:54 +0100 Subject: Fix preview zoom problems on windows Apparently the window is moved to random places on multi-monitor setups on windows. Fixes: QDS-263 Change-Id: I21082c7031fefff3057074c147e82df7a88f4f78 Reviewed-by: Ulf Hermann Reviewed-by: Tim Jenssen --- .../qmldbg_preview/qqmlpreviewhandler.cpp | 66 ++++------ .../qmltooling/qmldbg_preview/qqmlpreviewhandler.h | 2 + .../qmldbg_preview/qqmlpreviewposition.cpp | 146 ++++++++++++++++++--- .../qmldbg_preview/qqmlpreviewposition.h | 37 +++++- .../qml/debugger/qqmlpreview/tst_qqmlpreview.cpp | 12 +- 5 files changed, 189 insertions(+), 74 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp index 5bd96af582..5d2684b510 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp @@ -106,16 +106,9 @@ static void closeAllWindows() bool QQmlPreviewHandler::eventFilter(QObject *obj, QEvent *event) { - if (event->type() == QEvent::Show) { - if (QWindow *window = qobject_cast(obj)) { - m_lastPosition.initLastSavedWindowPosition(window); - } - } - if (m_currentWindow && (event->type() == QEvent::Move || event->type() == QEvent::Resize) && + if (m_currentWindow && (event->type() == QEvent::Move) && qobject_cast(obj) == m_currentWindow) { - // we always start with factor 1 so calculate and save the origin as it would be not scaled - m_lastPosition.setPosition(m_currentWindow->framePosition() * - QHighDpiScaling::factor(m_currentWindow)); + m_lastPosition.takePosition(m_currentWindow); } return QObject::eventFilter(obj, event); @@ -195,50 +188,39 @@ void QQmlPreviewHandler::rerun() } void QQmlPreviewHandler::zoom(qreal newFactor) +{ + m_zoomFactor = newFactor; + QTimer::singleShot(0, this, &QQmlPreviewHandler::doZoom); +} + +void QQmlPreviewHandler::doZoom() { if (!m_currentWindow) return; - if (qFuzzyIsNull(newFactor)) { + if (qFuzzyIsNull(m_zoomFactor)) { emit error(QString::fromLatin1("Zooming with factor: %1 will result in nothing " \ - "so it will be ignored.").arg(newFactor)); + "so it will be ignored.").arg(m_zoomFactor)); return; } - QString errorMessage; - bool resetZoom = false; - if (newFactor < 0) { + bool resetZoom = false; + if (m_zoomFactor < 0) { resetZoom = true; - newFactor = 1.0; + m_zoomFactor = 1.0; } - // On single-window devices we allow any scale factor as the window will adapt to the screen. - if (m_supportsMultipleWindows) { - const QSize newAvailableScreenSize = QQmlPreviewPosition::currentScreenSize(m_currentWindow) - * QHighDpiScaling::factor(m_currentWindow) / newFactor; - if (m_currentWindow->size().width() > newAvailableScreenSize.width()) { - errorMessage = QString::fromLatin1( - "Zooming with factor: " - "%1 will result in a too wide preview.").arg(newFactor); - } - if (m_currentWindow->size().height() > newAvailableScreenSize.height()) { - errorMessage = QString::fromLatin1( - "Zooming with factor: " - "%1 will result in a too heigh preview.").arg(newFactor); - } - } + m_currentWindow->setGeometry(m_currentWindow->geometry()); - if (errorMessage.isEmpty()) { - const QPoint newToOriginMappedPosition = m_currentWindow->position() * - QHighDpiScaling::factor(m_currentWindow) / newFactor; - m_currentWindow->destroy(); - QHighDpiScaling::setScreenFactor(m_currentWindow->screen(), newFactor); - if (resetZoom) - QHighDpiScaling::updateHighDpiScaling(); - m_currentWindow->setPosition(newToOriginMappedPosition); - m_currentWindow->show(); - } else { - emit error(errorMessage); - } + m_lastPosition.takePosition(m_currentWindow, QQmlPreviewPosition::InitializePosition); + m_currentWindow->destroy(); + + for (QScreen *screen : QGuiApplication::screens()) + QHighDpiScaling::setScreenFactor(screen, m_zoomFactor); + if (resetZoom) + QHighDpiScaling::updateHighDpiScaling(); + + m_currentWindow->show(); + m_lastPosition.initLastSavedWindowPosition(m_currentWindow); } void QQmlPreviewHandler::removeTranslators() diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h index 21ea672580..47491b9d8f 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h @@ -104,6 +104,7 @@ signals: protected: bool eventFilter(QObject *obj, QEvent *event); private: + void doZoom(); void tryCreateObject(); void showObject(QObject *object); void setCurrentWindow(QQuickWindow *window); @@ -121,6 +122,7 @@ private: QVector> m_createdObjects; QScopedPointer m_component; QPointer m_currentWindow; + qreal m_zoomFactor = 1.0; bool m_supportsMultipleWindows; QQmlPreviewPosition m_lastPosition; diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp index 3edcbac0a9..d4acd24da5 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp @@ -42,14 +42,47 @@ #include #include #include +#include QT_BEGIN_NAMESPACE -static const QSize availableScreenSize(const QPoint &point) +static QVector initScreensData() { - if (const QScreen *screen = QGuiApplication::screenAt(point)) - return screen->availableGeometry().size(); - return QSize(); + QVector screensData; + + for (QScreen *screen : QGuiApplication::screens()) { + QQmlPreviewPosition::ScreenData sd{screen->name(), screen->size()}; + screensData.append(sd); + } + return screensData; +} + +static QScreen *findScreen(const QString &nameOfScreen) +{ + for (QScreen *screen : QGuiApplication::screens()) { + if (screen->name() == nameOfScreen) + return screen; + } + return nullptr; +} + +static QDataStream &operator<<(QDataStream &out, const QQmlPreviewPosition::ScreenData &screenData) +{ + out << screenData.name; + out << screenData.size; + return out; +} + +static QDataStream &operator>>(QDataStream &in, QQmlPreviewPosition::ScreenData &screenData) +{ + in >> screenData.name; + in >> screenData.size; + return in; +} + +bool QQmlPreviewPosition::ScreenData::operator==(const QQmlPreviewPosition::ScreenData &other) const +{ + return other.size == size && other.name == name; } QQmlPreviewPosition::QQmlPreviewPosition() @@ -62,20 +95,36 @@ QQmlPreviewPosition::QQmlPreviewPosition() }); } -void QQmlPreviewPosition::setPosition(const QPoint &point) +QQmlPreviewPosition::~QQmlPreviewPosition() +{ + saveWindowPosition(); +} + +void QQmlPreviewPosition::takePosition(QWindow *window, InitializeState state) { - m_hasPosition = true; - m_lastWindowPosition = point; - m_savePositionTimer.start(); + Q_ASSERT(window); + // only save the position if we already tried to get the last saved position + if (m_initializeState == PositionInitialized) { + m_hasPosition = true; + auto screen = window->screen(); + auto nativePosition = QHighDpiScaling::mapPositionToNative(window->framePosition(), + screen->handle()); + m_lastWindowPosition = {screen->name(), nativePosition}; + + m_savePositionTimer.start(); + } + if (state == InitializePosition) + m_initializeState = InitializePosition; } void QQmlPreviewPosition::saveWindowPosition() { if (m_hasPosition) { + const QByteArray positionAsByteArray = fromPositionToByteArray(m_lastWindowPosition); if (!m_settingsKey.isNull()) - m_settings.setValue(m_settingsKey, m_lastWindowPosition); + m_settings.setValue(m_settingsKey, positionAsByteArray); - m_settings.setValue(QLatin1String("global_lastpostion"), m_lastWindowPosition); + m_settings.setValue(QLatin1String("global_lastpostion"), positionAsByteArray); } } @@ -85,29 +134,86 @@ void QQmlPreviewPosition::loadWindowPositionSettings(const QUrl &url) if (m_settings.contains(m_settingsKey)) { m_hasPosition = true; - m_lastWindowPosition = m_settings.value(m_settingsKey).toPoint(); + readLastPositionFromByteArray(m_settings.value(m_settingsKey).toByteArray()); } } void QQmlPreviewPosition::initLastSavedWindowPosition(QWindow *window) { - if (m_positionedWindows.contains(window)) - return; + Q_ASSERT(window); + m_initializeState = PositionInitialized; + if (m_currentInitScreensData.isEmpty()) + m_currentInitScreensData = initScreensData(); + // if it is the first time we just use the fall back from a last shown qml file if (!m_hasPosition) { - // in case there was nothing saved, we do not want to set anything if (!m_settings.contains(QLatin1String("global_lastpostion"))) return; - m_lastWindowPosition = m_settings.value(QLatin1String("global_lastpostion")).toPoint(); + readLastPositionFromByteArray(m_settings.value(QLatin1String("global_lastpostion")) + .toByteArray()); } - if (QGuiApplication::screenAt(m_lastWindowPosition)) - window->setFramePosition(m_lastWindowPosition); + setPosition(m_lastWindowPosition, window); +} + +QByteArray QQmlPreviewPosition::fromPositionToByteArray( + const QQmlPreviewPosition::Position &position) +{ + QByteArray array; + QDataStream stream(&array, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_5_12); + + const quint16 majorVersion = 1; + const quint16 minorVersion = 0; + + stream << majorVersion + << minorVersion + << m_currentInitScreensData + << position.screenName + << position.nativePosition; + return array; +} + +void QQmlPreviewPosition::readLastPositionFromByteArray(const QByteArray &array) +{ + QDataStream stream(array); + stream.setVersion(QDataStream::Qt_5_12); + + // no version check for 1.0 + //const quint16 currentMajorVersion = 1; + quint16 majorVersion = 0; + quint16 minorVersion = 0; - m_positionedWindows.append(window); + stream >> majorVersion >> minorVersion; + + QVector initScreensData; + stream >> initScreensData; + + if (m_currentInitScreensData != initScreensData) + return; + + QString nameOfScreen; + stream >> nameOfScreen; + + QScreen *screen = findScreen(nameOfScreen); + if (!screen) + return; + + QPoint nativePosition; + stream >> nativePosition; + if (nativePosition.isNull()) + return; + m_lastWindowPosition = {nameOfScreen, nativePosition}; } -const QSize QQmlPreviewPosition::currentScreenSize(QWindow *window) +void QQmlPreviewPosition::setPosition(const QQmlPreviewPosition::Position &position, + QWindow *window) { - return availableScreenSize(window->position()); + if (position.nativePosition.isNull()) + return; + if (QScreen *screen = findScreen(position.screenName)) { + window->setScreen(screen); + window->setFramePosition(QHighDpiScaling::mapPositionFromNative(position.nativePosition, + screen->handle())); + } } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h index 3d4ca9dc67..f403917f8c 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h @@ -56,6 +56,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -64,23 +66,46 @@ class QWindow; class QQmlPreviewPosition { public: + class ScreenData { + public: + bool operator==(const QQmlPreviewPosition::ScreenData &other) const; + QString name; + QSize size; + }; + class Position { + public: + QString screenName; + QPoint nativePosition; + }; + enum InitializeState { + InitializePosition, + PositionInitialized + }; + QQmlPreviewPosition(); + ~QQmlPreviewPosition(); - void setPosition(const QPoint &point); - void saveWindowPosition(); - void loadWindowPositionSettings(const QUrl &url); + + void takePosition(QWindow *window, InitializeState state = PositionInitialized); void initLastSavedWindowPosition(QWindow *window); - static const QSize currentScreenSize(QWindow *window); + void loadWindowPositionSettings(const QUrl &url); private: + void setPosition(const QQmlPreviewPosition::Position &position, QWindow *window); + QByteArray fromPositionToByteArray(const Position &position); + void readLastPositionFromByteArray(const QByteArray &array); + void saveWindowPosition(); + bool m_hasPosition = false; - QPoint m_lastWindowPosition; + InitializeState m_initializeState = InitializePosition; QSettings m_settings; QString m_settingsKey; QTimer m_savePositionTimer; + Position m_lastWindowPosition; QVector m_positionedWindows; -}; + QVector m_currentInitScreensData; +}; QT_END_NAMESPACE diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp index f713aa76c3..c7f8ec1118 100644 --- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp +++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp @@ -319,12 +319,12 @@ void tst_QQmlPreview::zoom() QTRY_VERIFY(m_files.contains(testFile(file))); float baseZoomFactor = -1; QTRY_VERIFY_WITH_TIMEOUT((baseZoomFactor = parseZoomFactor(m_process->output())) > 0, 30000); - m_client->triggerZoom(2.0f); - verifyZoomFactor(m_process, baseZoomFactor * 2.0f); - m_client->triggerZoom(1.5f); - verifyZoomFactor(m_process, baseZoomFactor * 1.5f); - m_client->triggerZoom(0.5f); - verifyZoomFactor(m_process, baseZoomFactor * 0.5f); + + for (auto testZoomFactor : {2.0f, 1.5f, 0.5f}) { + m_client->triggerZoom(testZoomFactor); + verifyZoomFactor(m_process, baseZoomFactor * testZoomFactor); + } + m_client->triggerZoom(-1.0f); verifyZoomFactor(m_process, baseZoomFactor); m_process->stop(); -- cgit v1.2.3