Allow dynamic and static registration of same URI in any order

Previously, a static registration would not go through if a dynamic
registration was carried out before the first type of the statically
registered module was referenced.

Change-Id: Icf6a2b78dff7d0e5b29138501e04723510d6a668
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2020-02-12 10:42:24 +01:00
parent bc3700d746
commit 2fae1d7e8f
5 changed files with 253 additions and 101 deletions

View File

@ -602,49 +602,57 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
scriptImported(blob, import->location, script.nameSpace, import->qualifier);
}
}
} else if (
// Major version of module already registered:
// We believe that the registration is complete.
QQmlMetaType::typeModule(import->uri, import->majorVersion)
// Otherwise, try to register further module types.
|| (qmldirResult != QQmlImports::QmldirInterceptedToRemote
&& QQmlMetaType::qmlRegisterModuleTypes(import->uri, import->majorVersion))
// Otherwise, there is no way to register any further types.
// Try with any module of that name.
|| QQmlMetaType::isAnyModule(import->uri)) {
if (!m_importCache.addLibraryImport(
importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, QString(), QString(), false, errors)) {
return false;
}
} else {
// Is this a module?
if (QQmlMetaType::isAnyModule(import->uri)
|| (qmldirResult != QQmlImports::QmldirInterceptedToRemote
&& QQmlMetaType::qmlRegisterModuleTypes(import->uri,
import->majorVersion))) {
// We haven't yet resolved this import
m_unresolvedImports << import;
QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor();
// Query any network import paths for this library.
// Interceptor might redirect local paths.
QStringList remotePathList = importDatabase->importPathList(
interceptor ? QQmlImportDatabase::LocalOrRemote
: QQmlImportDatabase::Remote);
if (!remotePathList.isEmpty()) {
// Add this library and request the possible locations for it
if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, QString(), QString(), false, errors))
import->minorVersion, QString(), QString(), true, errors))
return false;
} else {
// We haven't yet resolved this import
m_unresolvedImports << import;
QQmlAbstractUrlInterceptor *interceptor = typeLoader()->engine()->urlInterceptor();
// Query any network import paths for this library.
// Interceptor might redirect local paths.
QStringList remotePathList = importDatabase->importPathList(
interceptor ? QQmlImportDatabase::LocalOrRemote
: QQmlImportDatabase::Remote);
if (!remotePathList.isEmpty()) {
// Add this library and request the possible locations for it
if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion,
import->minorVersion, QString(), QString(), true, errors))
return false;
// Probe for all possible locations
int priority = 0;
const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion);
for (const QString &qmldirPath : qmlDirPaths) {
if (interceptor) {
QUrl url = interceptor->intercept(
QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
QQmlAbstractUrlInterceptor::QmldirFile);
if (!QQmlFile::isLocalFile(url)
&& !fetchQmldir(url, import, ++priority, errors)) {
return false;
}
} else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
// Probe for all possible locations
int priority = 0;
const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(import->uri, remotePathList, import->majorVersion, import->minorVersion);
for (const QString &qmldirPath : qmlDirPaths) {
if (interceptor) {
QUrl url = interceptor->intercept(
QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
QQmlAbstractUrlInterceptor::QmldirFile);
if (!QQmlFile::isLocalFile(url)
&& !fetchQmldir(url, import, ++priority, errors)) {
return false;
}
} else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
return false;
}
}
}
}

View File

@ -0,0 +1,27 @@
import QtQuick 2.12
import io.qt.bugreports 2.0
Item {
InterfaceConsumer2 {
objectName: "a1"
i: A2 {
property int i: 42
}
}
InterfaceConsumer2 {
objectName: "a2"
property A2 a: A2 {
property int i: 43
}
i: a
}
InterfaceConsumer2 {
objectName: "a3"
property A2 a: A2 {
id : aa
property int i: 44
}
i: aa
}
}

View File

@ -0,0 +1,160 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef INTERFACES_H
#define INTERFACES_H
#include <QtQml/qqml.h>
struct Interface {
};
QT_BEGIN_NAMESPACE
#define MyInterface_iid "io.qt.bugreports.Interface"
Q_DECLARE_INTERFACE(Interface, MyInterface_iid);
QT_END_NAMESPACE
class A : public QObject, Interface {
Q_OBJECT
Q_INTERFACES(Interface)
};
class B : public QObject, Interface {
Q_OBJECT
Q_INTERFACES(Interface)
};
class C : public QObject {
Q_OBJECT
};
struct Interface2
{
Q_GADGET
};
QT_BEGIN_NAMESPACE
#define MyInterface2_iid "io.qt.bugreports.Interface2"
Q_DECLARE_INTERFACE(Interface2, MyInterface2_iid);
QT_END_NAMESPACE
class A2 : public QObject, Interface2 {
Q_OBJECT
QML_ELEMENT
Q_INTERFACES(Interface2)
};
class B2 : public QObject, Interface2 {
Q_OBJECT
QML_ELEMENT
Q_INTERFACES(Interface2)
};
class C2 : public QObject {
Q_OBJECT
QML_ELEMENT
};
class InterfaceConsumer : public QObject {
Q_OBJECT
Q_PROPERTY(Interface *i READ interface WRITE setInterface NOTIFY interfaceChanged)
Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged)
public:
Interface* interface() const
{
return m_interface;
}
void setInterface(Interface* interface)
{
QObject* object = reinterpret_cast<QObject*>(interface);
m_testValue = object->property("i").toInt();
emit testValueChanged();
if (m_interface == interface)
return;
m_interface = interface;
emit interfaceChanged();
}
int testValue() {
return m_testValue;
}
signals:
void interfaceChanged();
void testValueChanged();
private:
Interface* m_interface = nullptr;
int m_testValue = 0;
};
class InterfaceConsumer2 : public QObject
{
Q_OBJECT
Q_PROPERTY(Interface2 *i READ interface WRITE setInterface NOTIFY interfaceChanged)
Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged)
QML_ELEMENT
public:
Interface2* interface() const
{
return m_interface;
}
void setInterface(Interface2* interface2)
{
QObject* object = reinterpret_cast<QObject*>(interface2);
m_testValue = object->property("i").toInt();
emit testValueChanged();
if (m_interface == interface2)
return;
m_interface = interface2;
emit interfaceChanged();
}
int testValue() {
return m_testValue;
}
signals:
void interfaceChanged();
void testValueChanged();
private:
Interface2 *m_interface = nullptr;
int m_testValue = 0;
};
#endif // INTERFACES_H

View File

@ -1,7 +1,10 @@
CONFIG += testcase
CONFIG += testcase qmltypes
TARGET = tst_qqmlproperty
macx:CONFIG -= app_bundle
QML_IMPORT_NAME = io.qt.bugreports
QML_IMPORT_VERSION = 2.0
SOURCES += tst_qqmlproperty.cpp
include (../../shared/util.pri)
@ -9,3 +12,6 @@ include (../../shared/util.pri)
TESTDATA = data/*
QT += core-private gui-private qml-private testlib
HEADERS += \
interfaces.h

View File

@ -25,6 +25,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
#include "interfaces.h"
#include <qtest.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@ -2090,74 +2092,22 @@ void tst_qqmlproperty::nullPropertyBinding()
QMetaObject::invokeMethod(root.get(), "tog");
}
struct Interface {
};
QT_BEGIN_NAMESPACE
#define MyInterface_iid "io.qt.bugreports.Interface"
Q_DECLARE_INTERFACE(Interface, MyInterface_iid);
QT_END_NAMESPACE
class A : public QObject, Interface {
Q_OBJECT
Q_INTERFACES(Interface)
};
class B : public QObject, Interface {
Q_OBJECT
Q_INTERFACES(Interface)
};
class C : public QObject {
Q_OBJECT
};
class InterfaceConsumer : public QObject {
Q_OBJECT
Q_PROPERTY(Interface* i READ interface WRITE setInterface NOTIFY interfaceChanged)
Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged)
public:
Interface* interface() const
{
return m_interface;
}
void setInterface(Interface* interface)
{
QObject* object = reinterpret_cast<QObject*>(interface);
m_testValue = object->property("i").toInt();
emit testValueChanged();
if (m_interface == interface)
return;
m_interface = interface;
emit interfaceChanged();
}
int testValue() {
return m_testValue;
}
signals:
void interfaceChanged();
void testValueChanged();
private:
Interface* m_interface = nullptr;
int m_testValue = 0;
};
void tst_qqmlproperty::interfaceBinding()
{
qmlRegisterInterface<Interface>("Interface");
qmlRegisterType<A>("io.qt.bugreports", 1, 0, "A");
qmlRegisterType<B>("io.qt.bugreports", 1, 0, "B");
qmlRegisterType<C>("io.qt.bugreports", 1, 0, "C");
qmlRegisterType<InterfaceConsumer>("io.qt.bugreports", 1, 0, "InterfaceConsumer");
qmlRegisterInterface<Interface>("Interface");
qmlRegisterType<A>("io.qt.bugreports", 1, 0, "A");
qmlRegisterType<B>("io.qt.bugreports", 1, 0, "B");
qmlRegisterType<C>("io.qt.bugreports", 1, 0, "C");
qmlRegisterType<InterfaceConsumer>("io.qt.bugreports", 1, 0, "InterfaceConsumer");
qmlRegisterInterface<Interface2>("Interface2");
const QUrl url = testFileUrl("interfaceBinding.qml");
const QVector<QUrl> urls = {
testFileUrl("interfaceBinding.qml"),
testFileUrl("interfaceBinding2.qml")
};
for (const QUrl &url : urls) {
QQmlEngine engine;
QQmlComponent component(&engine, url);
QScopedPointer<QObject> root(component.create());
@ -2165,6 +2115,7 @@ void tst_qqmlproperty::interfaceBinding()
QCOMPARE(root->findChild<QObject*>("a1")->property("testValue").toInt(), 42);
QCOMPARE(root->findChild<QObject*>("a2")->property("testValue").toInt(), 43);
QCOMPARE(root->findChild<QObject*>("a3")->property("testValue").toInt(), 44);
}
}
void tst_qqmlproperty::floatToStringPrecision_data()