aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlengine.cpp')
-rw-r--r--src/qml/qml/qqmlengine.cpp1803
1 files changed, 681 insertions, 1122 deletions
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 6e1365ea9f..62b7675e1d 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -1,67 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** 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 Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** 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-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlengine_p.h"
#include "qqmlengine.h"
-#include "qqmlcomponentattached_p.h"
#include "qqmlcontext_p.h"
#include "qqml.h"
#include "qqmlcontext.h"
-#include "qqmlexpression.h"
-#include "qqmlcomponent.h"
-#include "qqmlvme_p.h"
-#include "qqmlstringconverters_p.h"
#include "qqmlscriptstring.h"
#include "qqmlglobal_p.h"
-#include "qqmlcomponent_p.h"
-#include "qqmlextensioninterface.h"
-#include "qqmllist_p.h"
-#include "qqmltypenamecache_p.h"
#include "qqmlnotifier_p.h"
#include "qqmlincubator.h"
#include "qqmlabstracturlinterceptor.h"
+
#include <private/qqmldirparser_p.h>
#include <private/qqmlboundsignal_p.h>
#include <private/qqmljsdiagnosticmessage_p.h>
+#include <private/qqmltype_p_p.h>
+#include <private/qqmlpluginimporter_p.h>
#include <QtCore/qstandardpaths.h>
-#include <QtCore/qsettings.h>
#include <QtCore/qmetaobject.h>
#include <QDebug>
#include <QtCore/qcoreapplication.h>
@@ -70,11 +27,14 @@
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
#include <private/qthread_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <QtQml/private/qqmlcomponentattached_p.h>
+#include <QtQml/private/qqmlsourcecoordinate_p.h>
+#include <QtQml/private/qqmlcomponent_p.h>
#if QT_CONFIG(qml_network)
#include "qqmlnetworkaccessmanagerfactory.h"
#include <QNetworkAccessManager>
-#include <QtNetwork/qnetworkconfigmanager.h>
#endif
#include <private/qobject_p.h>
@@ -89,54 +49,20 @@
#endif
#include <private/qqmlplatform_p.h>
#include <private/qqmlloggingcategory_p.h>
+#include <private/qv4sequenceobject_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
# include <qt_windows.h>
-# ifndef Q_OS_WINRT
-# include <shlobj.h>
-# endif
+# include <shlobj.h>
# include <qlibrary.h>
# ifndef CSIDL_APPDATA
# define CSIDL_APPDATA 0x001a // <username>\Application Data
# endif
#endif // Q_OS_WIN
-Q_DECLARE_METATYPE(QQmlProperty)
-
QT_BEGIN_NAMESPACE
-// Declared in qqml.h
-int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
- const char *uri, int versionMajor,
- int versionMinor, const char *qmlName,
- const QString& reason)
-{
- QQmlPrivate::RegisterType type = {
- 0,
-
- 0,
- 0,
- 0,
- nullptr,
- reason,
-
- uri, versionMajor, versionMinor, qmlName, &staticMetaObject,
-
- QQmlAttachedPropertiesFunc(),
- nullptr,
-
- 0,
- 0,
- 0,
-
- nullptr, nullptr,
-
- nullptr,
- 0
- };
-
- return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
-}
+void qml_register_types_QML();
/*!
\qmltype QtObject
@@ -194,28 +120,9 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
\endcode
*/
-bool QQmlEnginePrivate::qml_debugging_enabled = false;
+Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
bool QQmlEnginePrivate::s_designerMode = false;
-void QQmlEnginePrivate::defineModule()
-{
- const char uri[] = "QtQml";
-
- qmlRegisterTypesAndRevisions<
- QObjectForeign,
-#if QT_CONFIG(qml_animation)
- QQmlTimer,
-#endif
-#if QT_CONFIG(qml_locale)
- QQmlLocale,
-#endif
- QQmlComponent,
- QQmlBind,
- QQmlConnections,
- QQmlLoggingCategory
- >(uri, 2);
-}
-
bool QQmlEnginePrivate::designerMode()
{
return s_designerMode;
@@ -290,440 +197,64 @@ QQmlImageProviderBase::~QQmlImageProviderBase()
{
}
-
-/*!
-\qmltype Qt
-\inqmlmodule QtQml
-\instantiates QQmlEnginePrivate
-\ingroup qml-utility-elements
-\keyword QmlGlobalQtObject
-\brief Provides a global object with useful enums and functions from Qt.
-
-The \c Qt object is a global object with utility functions, properties and enums.
-
-It is not instantiable; to use it, call the members of the global \c Qt object directly.
-For example:
-
-\qml
-import QtQuick 2.0
-
-Text {
- color: Qt.rgba(1, 0, 0, 1)
- text: Qt.md5("hello, world")
-}
-\endqml
-
-
-\section1 Enums
-
-The Qt object contains the enums available in the \l [QtCore]{Qt}{Qt Namespace}. For example, you can access
-the \l Qt::LeftButton and \l Qt::RightButton enumeration values as \c Qt.LeftButton and \c Qt.RightButton.
-
-
-\section1 Types
-
-The Qt object also contains helper functions for creating objects of specific
-data types. This is primarily useful when setting the properties of an item
-when the property has one of the following types:
-\list
-\li \c rect - use \l{Qt::rect()}{Qt.rect()}
-\li \c point - use \l{Qt::point()}{Qt.point()}
-\li \c size - use \l{Qt::size()}{Qt.size()}
-\endlist
-
-If the \c QtQuick module has been imported, the following helper functions for
-creating objects of specific data types are also available for clients to use:
-\list
-\li \c color - use \l{Qt::rgba()}{Qt.rgba()}, \l{Qt::hsla()}{Qt.hsla()}, \l{Qt::darker()}{Qt.darker()}, \l{Qt::lighter()}{Qt.lighter()} or \l{Qt::tint()}{Qt.tint()}
-\li \c font - use \l{Qt::font()}{Qt.font()}
-\li \c vector2d - use \l{Qt::vector2d()}{Qt.vector2d()}
-\li \c vector3d - use \l{Qt::vector3d()}{Qt.vector3d()}
-\li \c vector4d - use \l{Qt::vector4d()}{Qt.vector4d()}
-\li \c quaternion - use \l{Qt::quaternion()}{Qt.quaternion()}
-\li \c matrix4x4 - use \l{Qt::matrix4x4()}{Qt.matrix4x4()}
-\endlist
-
-There are also string based constructors for these types. See \l{qtqml-typesystem-basictypes.html}{QML Basic Types} for more information.
-
-\section1 Date/Time Formatters
-
-The Qt object contains several functions for formatting QDateTime, QDate and QTime values.
-
-\list
- \li \l{Qt::formatDateTime}{string Qt.formatDateTime(datetime date, variant format)}
- \li \l{Qt::formatDate}{string Qt.formatDate(datetime date, variant format)}
- \li \l{Qt::formatTime}{string Qt.formatTime(datetime date, variant format)}
-\endlist
-
-The format specification is described at \l{Qt::formatDateTime}{Qt.formatDateTime}.
-
-
-\section1 Dynamic Object Creation
-The following functions on the global object allow you to dynamically create QML
-items from files or strings. See \l{Dynamic QML Object Creation from JavaScript} for an overview
-of their use.
-
-\list
- \li \l{Qt::createComponent()}{object Qt.createComponent(url)}
- \li \l{Qt::createQmlObject()}{object Qt.createQmlObject(string qml, object parent, string filepath)}
-\endlist
-
-
-\section1 Other Functions
-
-The following functions are also on the Qt object.
-
-\list
- \li \l{Qt::quit()}{Qt.quit()}
- \li \l{Qt::md5()}{Qt.md5(string)}
- \li \l{Qt::btoa()}{string Qt.btoa(string)}
- \li \l{Qt::atob()}{string Qt.atob(string)}
- \li \l{Qt::binding()}{object Qt.binding(function)}
- \li \l{Qt::locale()}{object Qt.locale()}
- \li \l{Qt::resolvedUrl()}{string Qt.resolvedUrl(string)}
- \li \l{Qt::openUrlExternally()}{Qt.openUrlExternally(string)}
- \li \l{Qt::fontFamilies()}{list<string> Qt.fontFamilies()}
-\endlist
-*/
-
-/*!
- \qmlproperty object Qt::platform
- \since 5.1
-
- The \c platform object provides info about the underlying platform.
-
- Its properties are:
-
- \table
- \row
- \li \c platform.os
- \li
-
- This read-only property contains the name of the operating system.
-
- Possible values are:
-
- \list
- \li \c "android" - Android
- \li \c "ios" - iOS
- \li \c "tvos" - tvOS
- \li \c "linux" - Linux
- \li \c "osx" - \macos
- \li \c "qnx" - QNX (since Qt 5.9.3)
- \li \c "unix" - Other Unix-based OS
- \li \c "windows" - Windows
- \li \c "winrt" - WinRT / UWP
- \endlist
-
- \row
- \li \c platform.pluginName
- \li This is the name of the platform set on the QGuiApplication instance
- as returned by \l QGuiApplication::platformName()
-
- \endtable
-*/
-
-/*!
- \qmlproperty object Qt::application
- \since 5.1
-
- The \c application object provides access to global application state
- properties shared by many QML components.
-
- Its properties are:
-
- \table
- \row
- \li \c application.active
- \li
- Deprecated, use Qt.application.state == Qt.ApplicationActive instead.
-
- \row
- \li \c application.state
- \li
- This read-only property indicates the current state of the application.
-
- Possible values are:
-
- \list
- \li Qt.ApplicationActive - The application is the top-most and focused application, and the
- user is able to interact with the application.
- \li Qt.ApplicationInactive - The application is visible or partially visible, but not selected
- to be in front, the user cannot interact with the application.
- On desktop platforms, this typically means that the user activated
- another application. On mobile platforms, it is more common to
- enter this state when the OS is interrupting the user with for
- example incoming calls, SMS-messages or dialogs. This is usually a
- transient state during which the application is paused. The user
- may return focus to your application, but most of the time it will
- be the first indication that the application is going to be suspended.
- While in this state, consider pausing or stopping any activity that
- should not continue when the user cannot interact with your
- application, such as a video, a game, animations, or sensors.
- You should also avoid performing CPU-intensive tasks which might
- slow down the application in front.
- \li Qt.ApplicationSuspended - The application is suspended and not visible to the user. On
- mobile platforms, the application typically enters this state when
- the user returns to the home screen or switches to another
- application. While in this state, the application should ensure
- that the user perceives it as always alive and does not lose his
- progress, saving any persistent data. The application should cease
- all activities and be prepared for code execution to stop. While
- suspended, the application can be killed at any time without
- further warnings (for example when low memory forces the OS to purge
- suspended applications).
- \li Qt.ApplicationHidden - The application is hidden and runs in the background. This is the
- normal state for applications that need to do background processing,
- like playing music, while the user interacts with other applications.
- The application should free up all graphical resources when entering
- this state. A Qt Quick application should not usually handle this state
- at the QML level. Instead, you should unload the entire UI and reload
- the QML files whenever the application becomes active again.
- \endlist
-
- \row
- \li \c application.layoutDirection
- \li
- This read-only property can be used to query the default layout direction of the
- application. On system start-up, the default layout direction depends on the
- application's language. The property has a value of \c Qt.RightToLeft in locales
- where text and graphic elements are read from right to left, and \c Qt.LeftToRight
- where the reading direction flows from left to right. You can bind to this
- property to customize your application layouts to support both layout directions.
-
- Possible values are:
-
- \list
- \li Qt.LeftToRight - Text and graphics elements should be positioned
- from left to right.
- \li Qt.RightToLeft - Text and graphics elements should be positioned
- from right to left.
- \endlist
- \row
- \li \c application.font
- \li This read-only property holds the default application font as
- returned by \l QGuiApplication::font().
- \row
- \li \c application.arguments
- \li This is a string list of the arguments the executable was invoked with.
- \row
- \li \c application.name
- \li This is the application name set on the QCoreApplication instance. This property can be written
- to in order to set the application name.
- \row
- \li \c application.displayName (since Qt 5.9)
- \li This is the application display name set on the QGuiApplication instance. This property can be written
- to in order to set the application display name.
- \row
- \li \c application.version
- \li This is the application version set on the QCoreApplication instance. This property can be written
- to in order to set the application version.
- \row
- \li \c application.organization
- \li This is the organization name set on the QCoreApplication instance. This property can be written
- to in order to set the organization name.
- \row
- \li \c application.domain
- \li This is the organization domain set on the QCoreApplication instance. This property can be written
- to in order to set the organization domain.
-
- \row
- \li \c application.supportsMultipleWindows
- \li This read-only property can be used to determine whether or not the
- platform supports multiple windows. Some embedded platforms do not support
- multiple windows, for example.
-
- \row
- \li \c application.screens
- \li An array containing the descriptions of all connected screens. The
- elements of the array are objects with the same properties as the
- \l{Screen} attached object. In practice the array corresponds to the screen
- list returned by QGuiApplication::screens(). In addition to examining
- properties like name, width, height, etc., the array elements can also be
- assigned to the screen property of Window items, thus serving as an
- alternative to the C++ side's QWindow::setScreen(). This property has been
- added in Qt 5.9.
-
- \endtable
-
- The object also has one signal, aboutToQuit(), which is the same as \l QCoreApplication::aboutToQuit().
-
- The following example uses the \c application object to indicate
- whether the application is currently active:
-
- \snippet qml/application.qml document
-
- Note that when using QML without a QGuiApplication, the following properties will be undefined:
- \list
- \li application.active
- \li application.state
- \li application.layoutDirection
- \li application.font
- \endlist
-
- \sa Screen, Window, {QtQuick.Window::Window::screen}{Window.screen}
-*/
-
-/*!
- \qmlproperty object Qt::inputMethod
- \since 5.0
-
- The \c inputMethod object allows access to application's QInputMethod object
- and all its properties and slots. See the QInputMethod documentation for
- further details.
-*/
-
-/*!
- \qmlproperty object Qt::styleHints
- \since 5.5
-
- The \c styleHints object provides platform-specific style hints and settings.
- See the QStyleHints documentation for further details.
-
- \note The \c styleHints object is only available when using the Qt Quick module.
-
- The following example uses the \c styleHints object to determine whether an
- item should gain focus on mouse press or touch release:
- \code
- import QtQuick 2.4
-
- MouseArea {
- id: button
-
- onPressed: {
- if (!Qt.styleHints.setFocusOnTouchRelease)
- button.forceActiveFocus()
- }
- onReleased: {
- if (Qt.styleHints.setFocusOnTouchRelease)
- button.forceActiveFocus()
- }
- }
- \endcode
-*/
-
-/*!
-\qmlmethod object Qt::include(string url, jsobject callback)
-\deprecated
-
-This method should not be used. Use ECMAScript modules instead and the native
-JavaScript \c import and \c export statements instead.
-
-Includes another JavaScript file. This method can only be used from within JavaScript files,
-and not regular QML files.
-
-This imports all functions from \a url into the current script's namespace.
-
-Qt.include() returns an object that describes the status of the operation. The object has
-a single property, \c {status}, that is set to one of the following values:
-
-\table
-\header \li Symbol \li Value \li Description
-\row \li result.OK \li 0 \li The include completed successfully.
-\row \li result.LOADING \li 1 \li Data is being loaded from the network.
-\row \li result.NETWORK_ERROR \li 2 \li A network error occurred while fetching the url.
-\row \li result.EXCEPTION \li 3 \li A JavaScript exception occurred while executing the included code.
-An additional \c exception property will be set in this case.
-\endtable
-
-The \c status property will be updated as the operation progresses.
-
-If provided, \a callback is invoked when the operation completes. The callback is passed
-the same object as is returned from the Qt.include() call.
-*/
-// Qt.include() is implemented in qv4include.cpp
-
-QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e)
-: propertyCapture(nullptr), rootContext(nullptr),
-#if QT_CONFIG(qml_debug)
- profiler(nullptr),
-#endif
- outputWarningsToMsgLog(true),
- cleanup(nullptr), erroredBindings(nullptr), inProgressCreations(0),
-#if QT_CONFIG(qml_worker_script)
- workerScriptEngine(nullptr),
-#endif
- activeObjectCreator(nullptr),
-#if QT_CONFIG(qml_network)
- networkAccessManager(nullptr), networkAccessManagerFactory(nullptr),
-#endif
- urlInterceptor(nullptr), scarceResourcesRefCount(0), importDatabase(e), typeLoader(e),
- uniqueId(1), incubatorCount(0), incubationController(nullptr)
-{
-}
-
QQmlEnginePrivate::~QQmlEnginePrivate()
{
if (inProgressCreations)
qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations);
- while (cleanup) {
- QQmlCleanup *c = cleanup;
- cleanup = c->next;
- if (cleanup) cleanup->prev = &cleanup;
- c->next = nullptr;
- c->prev = nullptr;
- c->clear();
- }
-
- doDeleteInEngineThread();
-
if (incubationController) incubationController->d = nullptr;
incubationController = nullptr;
QQmlMetaType::freeUnusedTypesAndCaches();
- for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) {
- iter.value()->isRegisteredWithEngine = false;
-
- // since unregisterInternalCompositeType() will not be called in this
- // case, we have to clean up the type registration manually
- QMetaType::unregisterType(iter.value()->metaTypeId);
- QMetaType::unregisterType(iter.value()->listMetaTypeId);
- }
#if QT_CONFIG(qml_debug)
delete profiler;
#endif
+ qDeleteAll(cachedValueTypeInstances);
}
void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
{
- if (QQmlData *d = QQmlData::get(o)) {
+ QObjectPrivate *p = QObjectPrivate::get(o);
+ if (QQmlData *d = QQmlData::get(p)) {
+ const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
if (d->ownContext) {
- for (QQmlContextData *lc = d->ownContext->linkedContext; lc; lc = lc->linkedContext) {
- lc->invalidate();
- if (lc->contextObject == o)
- lc->contextObject = nullptr;
- }
- d->ownContext->invalidate();
- if (d->ownContext->contextObject == o)
- d->ownContext->contextObject = nullptr;
- d->ownContext = nullptr;
+ d->ownContext->deepClearContextObject(o, invalidate, invalidate);
+ d->ownContext.reset();
d->context = nullptr;
+ Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o);
+ } else if (d->outerContext && d->outerContext->contextObject() == o) {
+ d->outerContext->deepClearContextObject(o, invalidate, invalidate);
}
- if (d->outerContext && d->outerContext->contextObject == o)
- d->outerContext->contextObject = nullptr;
+ if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
+ // This is somewhat dangerous because another thread might concurrently
+ // try to resolve the dynamic metaobject. In practice this will then
+ // lead to either the code path that still returns the interceptor
+ // metaobject or the code path that returns the string casted one. Both
+ // is fine if you cannot actually touch the object itself. Since the
+ // other thread is obviously not synchronized to this one, it can't.
+ //
+ // In particular we do this when delivering the frameSwapped() signal
+ // in QQuickWindow. The handler for frameSwapped() is written in a way
+ // that is thread safe as long as QQuickWindow's dtor hasn't finished.
+ // QQuickWindow's dtor does synchronize with the render thread, but it
+ // runs _after_ qdeclarativeelement_destructor.
+ static_cast<QQmlInterceptorMetaObject *>(p->metaObject)->invalidate();
+ d->hasVMEMetaObject = d->hasInterceptorMetaObject = false;
+ }
// Mark this object as in the process of deletion to
// prevent it resolving in bindings
QQmlData::markAsDeleted(o);
-
- // Disconnect the notifiers now - during object destruction this would be too late, since
- // the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to
- // get the metaobject anymore.
- d->disconnectNotifiers();
}
}
-QQmlData::QQmlData()
- : ownedByQml1(false), ownMemory(true), indestructible(true), explicitIndestructibleSet(false),
+QQmlData::QQmlData(Ownership ownership)
+ : ownMemory(ownership == OwnsMemory), indestructible(true), explicitIndestructibleSet(false),
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
- hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false),
- bindingBitsArraySize(InlineBindingArraySize), notifyList(nullptr),
- bindings(nullptr), signalHandlers(nullptr), nextContextObject(nullptr), prevContextObject(nullptr),
- lineNumber(0), columnNumber(0), jsEngineId(0),
- propertyCache(nullptr), guards(nullptr), extendedData(nullptr)
+ hasInterceptorMetaObject(false), hasVMEMetaObject(false), hasConstWrapper(false), dummy(0),
+ bindingBitsArraySize(InlineBindingArraySize)
{
memset(bindingBitsValue, 0, sizeof(bindingBitsValue));
init();
@@ -736,18 +267,9 @@ QQmlData::~QQmlData()
void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o)
{
QQmlData *ddata = static_cast<QQmlData *>(d);
- if (ddata->ownedByQml1)
- return;
ddata->destroyed(o);
}
-void QQmlData::parentChanged(QAbstractDeclarativeData *d, QObject *o, QObject *p)
-{
- QQmlData *ddata = static_cast<QQmlData *>(d);
- if (ddata->ownedByQml1)
- return;
- ddata->parentChanged(o, p);
-}
class QQmlThreadNotifierProxyObject : public QObject
{
@@ -775,7 +297,6 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
{
QQmlData *ddata = QQmlData::get(object, false);
if (!ddata) return; // Probably being deleted
- if (ddata->ownedByQml1) return;
// In general, QML only supports QObject's that live on the same thread as the QQmlEngine
// that they're exposed to. However, to make writing "worker objects" that calculate data
@@ -783,43 +304,48 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (ddata->notifyList &&
- QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.loadRelaxed()) {
- if (!QObjectPrivate::get(object)->threadData->thread.loadAcquire())
+ // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
+ // but that should be rare.
+ if (!ddata->notifyList.loadRelaxed())
+ return;
+
+ auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
+ if (QThread::currentThreadId() != objectThreadData->threadId.loadRelaxed()) {
+ if (!objectThreadData->thread.loadAcquire())
return;
QMetaMethod m = QMetaObjectPrivate::signal(object->metaObject(), index);
QList<QByteArray> parameterTypes = m.parameterTypes();
- QScopedPointer<QMetaCallEvent> ev(new QMetaCallEvent(m.methodIndex(), 0, nullptr,
- object, index,
- parameterTypes.count() + 1));
+ auto ev = std::make_unique<QMetaCallEvent>(m.methodIndex(), 0, nullptr,
+ object, index,
+ parameterTypes.size() + 1);
void **args = ev->args();
- int *types = ev->types();
+ QMetaType *types = ev->types();
- for (int ii = 0; ii < parameterTypes.count(); ++ii) {
+ for (int ii = 0; ii < parameterTypes.size(); ++ii) {
const QByteArray &typeName = parameterTypes.at(ii);
if (typeName.endsWith('*'))
- types[ii + 1] = QMetaType::VoidStar;
+ types[ii + 1] = QMetaType(QMetaType::VoidStar);
else
- types[ii + 1] = QMetaType::type(typeName);
+ types[ii + 1] = QMetaType::fromName(typeName);
- if (!types[ii + 1]) {
+ if (!types[ii + 1].isValid()) {
qWarning("QObject::connect: Cannot queue arguments of type '%s'\n"
"(Make sure '%s' is registered using qRegisterMetaType().)",
typeName.constData(), typeName.constData());
return;
}
- args[ii + 1] = QMetaType::create(types[ii + 1], a[ii + 1]);
+ args[ii + 1] = types[ii + 1].create(a[ii + 1]);
}
QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject;
mpo->target = object;
- mpo->moveToThread(QObjectPrivate::get(object)->threadData->thread.loadAcquire());
- QCoreApplication::postEvent(mpo, ev.take());
+ mpo->moveToThread(objectThreadData->thread.loadAcquire());
+ QCoreApplication::postEvent(mpo, ev.release());
} else {
QQmlNotifierEndpoint *ep = ddata->notify(index);
@@ -830,16 +356,12 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
int QQmlData::receivers(QAbstractDeclarativeData *d, const QObject *, int index)
{
QQmlData *ddata = static_cast<QQmlData *>(d);
- if (ddata->ownedByQml1)
- return 0;
return ddata->endpointCount(index);
}
bool QQmlData::isSignalConnected(QAbstractDeclarativeData *d, const QObject *, int index)
{
QQmlData *ddata = static_cast<QQmlData *>(d);
- if (ddata->ownedByQml1)
- return false;
return ddata->signalHasEndpoint(index);
}
@@ -859,11 +381,15 @@ int QQmlData::endpointCount(int index)
void QQmlData::markAsDeleted(QObject *o)
{
- QQmlData::setQueuedForDeletion(o);
-
- QObjectPrivate *p = QObjectPrivate::get(o);
- for (QList<QObject *>::const_iterator it = p->children.constBegin(), end = p->children.constEnd(); it != end; ++it) {
- QQmlData::markAsDeleted(*it);
+ QVarLengthArray<QObject *> workStack;
+ workStack.push_back(o);
+ while (!workStack.isEmpty()) {
+ auto currentObject = workStack.last();
+ workStack.pop_back();
+ QQmlData::setQueuedForDeletion(currentObject);
+ auto currentObjectPriv = QObjectPrivate::get(currentObject);
+ for (QObject *child: std::as_const(currentObjectPriv->children))
+ workStack.push_back(child);
}
}
@@ -872,40 +398,70 @@ void QQmlData::setQueuedForDeletion(QObject *object)
if (object) {
if (QQmlData *ddata = QQmlData::get(object)) {
if (ddata->ownContext) {
- Q_ASSERT(ddata->ownContext == ddata->context);
- ddata->context->emitDestruction();
- if (ddata->ownContext->contextObject == object)
- ddata->ownContext->contextObject = nullptr;
- ddata->ownContext = nullptr;
+ Q_ASSERT(ddata->ownContext.data() == ddata->context);
+ ddata->ownContext->deepClearContextObject(object);
+ ddata->ownContext.reset();
ddata->context = nullptr;
}
ddata->isQueuedForDeletion = true;
+
+ // Disconnect the notifiers now - during object destruction this would be too late,
+ // since the disconnect call wouldn't be able to call disconnectNotify(), as it isn't
+ // possible to get the metaobject anymore.
+ // Also, there is no point in evaluating bindings in order to set properties on
+ // half-deleted objects.
+ ddata->disconnectNotifiers(DeleteNotifyList::No);
}
}
}
-void QQmlData::flushPendingBindingImpl(QQmlPropertyIndex index)
+void QQmlData::flushPendingBinding(int coreIndex)
{
- clearPendingBindingBit(index.coreIndex());
+ clearPendingBindingBit(coreIndex);
// Find the binding
QQmlAbstractBinding *b = bindings;
- while (b && (b->targetPropertyIndex().coreIndex() != index.coreIndex() ||
+ while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
b->targetPropertyIndex().hasValueTypeIndex()))
b = b->nextBinding();
- if (b && b->targetPropertyIndex().coreIndex() == index.coreIndex() &&
+ if (b && b->targetPropertyIndex().coreIndex() == coreIndex &&
!b->targetPropertyIndex().hasValueTypeIndex())
b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
QQmlPropertyData::DontRemoveBinding);
}
-QQmlData::DeferredData::DeferredData()
-{
-}
+QQmlData::DeferredData::DeferredData() = default;
+QQmlData::DeferredData::~DeferredData() = default;
-QQmlData::DeferredData::~DeferredData()
+template<>
+int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
+ QQmlPrivate::RegisterType type = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ QMetaType(),
+ QMetaType(),
+ 0, nullptr, nullptr,
+ QString(),
+ nullptr,
+ uri,
+ QTypeRevision::fromVersion(versionMajor, versionMinor),
+ qmlName,
+ nullptr,
+ nullptr,
+ nullptr,
+ -1,
+ -1,
+ -1,
+ nullptr,
+ nullptr,
+ nullptr,
+ QTypeRevision::zero(),
+ -1,
+ QQmlPrivate::ValueTypeCreationMethod::None,
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
bool QQmlEnginePrivate::baseModulesUninitialized = true;
@@ -914,23 +470,25 @@ void QQmlEnginePrivate::init()
Q_Q(QQmlEngine);
if (baseModulesUninitialized) {
+ // Register builtins
+ qml_register_types_QML();
+
+ // No need to specifically register those.
+ static_assert(std::is_same_v<QStringList, QList<QString>>);
+ static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
- // required for the Compiler.
- qmlRegisterType<QObject>("QML", 1, 0, "QtObject");
- qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component");
+ qRegisterMetaType<QQmlScriptString>();
+ qRegisterMetaType<QQmlComponent::Status>();
+ qRegisterMetaType<QList<QObject*> >();
+ qRegisterMetaType<QQmlBinding*>();
+
+ // Protect the module: We don't want any URL interceptor to mess with the builtins.
+ qmlProtectModule("QML", 1);
QQmlData::init();
baseModulesUninitialized = false;
}
- qRegisterMetaType<QVariant>();
- qRegisterMetaType<QQmlScriptString>();
- qRegisterMetaType<QJSValue>();
- qRegisterMetaType<QQmlComponent::Status>();
- qRegisterMetaType<QList<QObject*> >();
- qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QQmlBinding*>();
-
q->handle()->setQmlEngine(q);
rootContext = new QQmlContext(q,true);
@@ -942,29 +500,16 @@ void QQmlEnginePrivate::init()
\inmodule QtQml
\brief The QQmlEngine class provides an environment for instantiating QML components.
- Each QML component is instantiated in a QQmlContext.
- QQmlContext's are essential for passing data to QML
- components. In QML, contexts are arranged hierarchically and this
- hierarchy is managed by the QQmlEngine.
+ A QQmlEngine is used to manage \l{QQmlComponent}{components} and objects created from
+ them and execute their bindings and functions. QQmlEngine also inherits from
+ \l{QJSEngine} which allows seamless integration between your QML components and
+ JavaScript code.
- Prior to creating any QML components, an application must have
- created a QQmlEngine to gain access to a QML context. The
- following example shows how to create a simple Text item.
-
- \code
- QQmlEngine engine;
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0\nText { text: \"Hello world!\" }", QUrl());
- QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
+ Each QML component is instantiated in a QQmlContext. In QML, contexts are arranged
+ hierarchically and this hierarchy is managed by the QQmlEngine. By default,
+ components are instantiated in the \l {QQmlEngine::rootContext()}{root context}.
- //add item to view, etc
- ...
- \endcode
-
- In this case, the Text item will be created in the engine's
- \l {QQmlEngine::rootContext()}{root context}.
-
- \sa QQmlComponent, QQmlContext, {QML Global Object}
+ \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
*/
/*!
@@ -995,30 +540,29 @@ QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent)
invalidated, but not destroyed (unless they are parented to the
QQmlEngine object).
- See QJSEngine docs for details on cleaning up the JS engine.
+ See ~QJSEngine() for details on cleaning up the JS engine.
*/
QQmlEngine::~QQmlEngine()
{
Q_D(QQmlEngine);
+ handle()->inShutdown = true;
QJSEnginePrivate::removeFromDebugServer(this);
- d->typeLoader.invalidate();
-
// Emit onDestruction signals for the root context before
// we destroy the contexts, engine, Singleton Types etc. that
// may be required to handle the destruction signal.
- QQmlContextData::get(rootContext())->emitDestruction();
+ QQmlContextPrivate::get(rootContext())->emitDestruction();
// clean up all singleton type instances which we own.
// we do this here and not in the private dtor since otherwise a crash can
// occur (if we are the QObject parent of the QObject singleton instance)
// XXX TODO: performance -- store list of singleton types separately?
- QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes();
- for (const QQmlType &currType : singletonTypes)
- d->destroySingletonInstance(currType);
+ d->singletonInstances.clear();
delete d->rootContext;
d->rootContext = nullptr;
+
+ d->typeLoader.invalidate();
}
/*! \fn void QQmlEngine::quit()
@@ -1055,14 +599,35 @@ QQmlEngine::~QQmlEngine()
Once the component cache has been cleared, components must be loaded before
any new objects can be created.
- \sa trimComponentCache()
+ \note Any existing objects created from QML components retain their types,
+ even if you clear the component cache. This includes singleton objects. If you
+ create more objects from the same QML code after clearing the cache, the new
+ objects will be of different types than the old ones. Assigning such a new
+ object to a property of its declared type belonging to an object created
+ before clearing the cache won't work.
+
+ As a general rule of thumb, make sure that no objects created from QML
+ components are alive when you clear the component cache.
+
+ \sa trimComponentCache(), clearSingletons()
*/
void QQmlEngine::clearComponentCache()
{
Q_D(QQmlEngine);
+
+ // Contexts can hold on to CUs but live on the JS heap.
+ // Use a non-incremental GC run to get rid of those.
+ QV4::MemoryManager *mm = handle()->memoryManager;
+ auto oldLimit = mm->gcStateMachine->timeLimit;
+ mm->setGCTimeLimit(-1);
+ mm->runGC();
+ mm->gcStateMachine->timeLimit = std::move(oldLimit);
+
+ handle()->clearCompilationUnits();
d->typeLoader.lock();
d->typeLoader.clearCache();
d->typeLoader.unlock();
+ QQmlMetaType::freeUnusedTypesAndCaches();
}
/*!
@@ -1080,10 +645,32 @@ void QQmlEngine::clearComponentCache()
void QQmlEngine::trimComponentCache()
{
Q_D(QQmlEngine);
+ handle()->trimCompilationUnits();
d->typeLoader.trimCache();
}
/*!
+ Clears all singletons the engine owns.
+
+ This function drops all singleton instances, deleting any QObjects owned by
+ the engine among them. This is useful to make sure that no QML-created objects
+ are left before calling clearComponentCache().
+
+ QML properties holding QObject-based singleton instances become null if the
+ engine owns the singleton or retain their value if the engine doesn't own it.
+ The singletons are not automatically re-created by accessing existing
+ QML-created objects. Only when new components are instantiated, the singletons
+ are re-created.
+
+ \sa clearComponentCache()
+ */
+void QQmlEngine::clearSingletons()
+{
+ Q_D(QQmlEngine);
+ d->singletonInstances.clear();
+}
+
+/*!
Returns the engine's root context.
The root context is automatically created by the QQmlEngine.
@@ -1100,48 +687,77 @@ QQmlContext *QQmlEngine::rootContext() const
return d->rootContext;
}
+#if QT_DEPRECATED_SINCE(6, 0)
/*!
\internal
+ \deprecated
This API is private for 5.1
- Sets the \a urlInterceptor to be used when resolving URLs in QML.
+ Returns the last QQmlAbstractUrlInterceptor. It must not be modified outside
+ the GUI thread.
+*/
+QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const
+{
+ Q_D(const QQmlEngine);
+ return d->urlInterceptors.last();
+}
+#endif
+
+/*!
+ Adds a \a urlInterceptor to be used when resolving URLs in QML.
This also applies to URLs used for loading script files and QML types.
- This should not be modifed while the engine is loading files, or URL
- selection may be inconsistent.
+ The URL interceptors should not be modifed while the engine is loading files,
+ or URL selection may be inconsistent. Multiple URL interceptors, when given,
+ will be called in the order they were added for each URL.
+
+ QQmlEngine does not take ownership of the interceptor and won't delete it.
*/
-void QQmlEngine::setUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
+void QQmlEngine::addUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
{
Q_D(QQmlEngine);
- d->urlInterceptor = urlInterceptor;
+ d->urlInterceptors.append(urlInterceptor);
}
/*!
- \internal
- This API is private for 5.1
+ Remove a \a urlInterceptor that was previously added using
+ \l addUrlInterceptor. The URL interceptors should not be modifed while the
+ engine is loading files, or URL selection may be inconsistent.
- Returns the current QQmlAbstractUrlInterceptor. It must not be modified outside
- the GUI thread.
+ This does not delete the interceptor, but merely removes it from the engine.
+ You can re-use it on the same or a different engine afterwards.
*/
-QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const
+void QQmlEngine::removeUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
+{
+ Q_D(QQmlEngine);
+ d->urlInterceptors.removeOne(urlInterceptor);
+}
+
+/*!
+ Run the current URL interceptors on the given \a url of the given \a type and
+ return the result.
+ */
+QUrl QQmlEngine::interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const
{
Q_D(const QQmlEngine);
- return d->urlInterceptor;
+ QUrl result = url;
+ for (QQmlAbstractUrlInterceptor *interceptor : d->urlInterceptors)
+ result = interceptor->intercept(result, type);
+ return result;
}
-void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index)
+/*!
+ Returns the list of currently active URL interceptors.
+ */
+QList<QQmlAbstractUrlInterceptor *> QQmlEngine::urlInterceptors() const
{
- if (activeObjectCreator) {
- activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index));
- } else {
- void *args[] = { nullptr };
- QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args);
- }
+ Q_D(const QQmlEngine);
+ return d->urlInterceptors;
}
QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const
{
const QString providerIdLower = providerId.toLower();
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&imageProviderMutex);
return imageProviders.value(providerIdLower);
}
@@ -1236,7 +852,7 @@ void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBa
Q_D(QQmlEngine);
QString providerIdLower = providerId.toLower();
QSharedPointer<QQmlImageProviderBase> sp(provider);
- QMutexLocker locker(&d->mutex);
+ QMutexLocker locker(&d->imageProviderMutex);
d->imageProviders.insert(std::move(providerIdLower), std::move(sp));
}
@@ -1249,7 +865,7 @@ QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) cons
{
Q_D(const QQmlEngine);
const QString providerIdLower = providerId.toLower();
- QMutexLocker locker(&d->mutex);
+ QMutexLocker locker(&d->imageProviderMutex);
return d->imageProviders.value(providerIdLower).data();
}
@@ -1262,7 +878,7 @@ void QQmlEngine::removeImageProvider(const QString &providerId)
{
Q_D(QQmlEngine);
const QString providerIdLower = providerId.toLower();
- QMutexLocker locker(&d->mutex);
+ QMutexLocker locker(&d->imageProviderMutex);
d->imageProviders.take(providerIdLower);
}
@@ -1280,7 +896,9 @@ QUrl QQmlEngine::baseUrl() const
{
Q_D(const QQmlEngine);
if (d->baseUrl.isEmpty()) {
- return QUrl::fromLocalFile(QDir::currentPath() + QDir::separator());
+ const QString currentPath = QDir::currentPath();
+ const QString rootPath = QDir::rootPath();
+ return QUrl::fromLocalFile((currentPath == rootPath) ? rootPath : (currentPath + QDir::separator()));
} else {
return d->baseUrl;
}
@@ -1325,6 +943,75 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
d->outputWarningsToMsgLog = enabled;
}
+
+/*!
+ \since 6.6
+ If this method is called inside of a function that is part of
+ a binding in QML, the binding will be treated as a translation binding.
+
+ \code
+ class I18nAwareClass : public QObject {
+
+ //...
+
+ QString text() const
+ {
+ if (auto engine = qmlEngine(this))
+ engine->markCurrentFunctionAsTranslationBinding();
+ return tr("Hello, world!");
+ }
+ };
+ \endcode
+
+ \note This function is mostly useful if you wish to provide your
+ own alternative to the qsTr function. To ensure that properties
+ exposed from C++ classes are updated on language changes, it is
+ instead recommended to react to \c LanguageChange events. That
+ is a more general mechanism which also works when the class is
+ used in a non-QML context, and has slightly less overhead. However,
+ using \c markCurrentFunctionAsTranslationBinding can be acceptable
+ when the class is already closely tied to the QML engine.
+ For more details, see \l {Prepare for Dynamic Language Changes}
+
+ \sa QQmlEngine::retranslate
+*/
+void QQmlEngine::markCurrentFunctionAsTranslationBinding()
+{
+ Q_D(QQmlEngine);
+ if (auto propertyCapture = d->propertyCapture)
+ propertyCapture->captureTranslation();
+}
+
+/*!
+ \internal
+
+ Capture the given property as part of a binding.
+ */
+void QQmlEngine::captureProperty(QObject *object, const QMetaProperty &property) const
+{
+ Q_D(const QQmlEngine);
+ if (d->propertyCapture && !property.isConstant()) {
+ d->propertyCapture->captureProperty(
+ object, property.propertyIndex(),
+ QMetaObjectPrivate::signalIndex(property.notifySignal()));
+ }
+}
+
+/*!
+ \qmlproperty string Qt::uiLanguage
+ \since 5.15
+
+ The uiLanguage holds the name of the language to be used for user interface
+ string translations. It is exposed in C++ as QQmlEngine::uiLanguage property.
+
+ You can set the value freely and use it in bindings. It is recommended to set it
+ after installing translators in your application. By convention, an empty string
+ means no translation from the language used in the source code is intended to occur.
+
+ If you're using QQmlApplicationEngine and the value changes, QQmlEngine::retranslate()
+ will be called.
+*/
+
/*!
\fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId)
@@ -1336,43 +1023,30 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
type, either a default constructed QJSValue or a \c nullptr is returned.
QObject* example:
- \code
- class MySingleton : public QObject {
- Q_OBJECT
- static int typeId;
- // ...
- };
- // Register with QObject* callback
- MySingleton::typeId = qmlRegisterSingletonType<MySingleton>(...);
-
- // Retrieve as QObject*
- QQmlEngine engine;
- MySingleton* instance = engine.singletonInstance<MySingleton*>(MySingleton::typeId);
- \endcode
+ \snippet code/src_qml_qqmlengine.cpp 0
+ \codeline
+ \snippet code/src_qml_qqmlengine.cpp 1
+ \codeline
+ \snippet code/src_qml_qqmlengine.cpp 2
QJSValue example:
- \code
- // Register with QJSValue callback
- int typeId = qmlRegisterSingletonType(...);
- // Retrieve as QJSValue
- QQmlEngine engine;
- QJSValue instance = engine.singletonInstance<QJSValue>(typeId);
- \endcode
+ \snippet code/src_qml_qqmlengine.cpp 3
+ \codeline
+ \snippet code/src_qml_qqmlengine.cpp 4
- It is recommended to store the QML type id during registration, e.g. as a static member
- in the singleton class. Otherwise, a costly lookup via qmlTypeId() has to be performed
- at run-time.
+ It is recommended to store the QML type id, e.g. as a static member in the
+ singleton class. The lookup via qmlTypeId() is costly.
- \sa qmlRegisterSingletonType(), qmlTypeId()
+ \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
\since 5.12
*/
template<>
QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
{
Q_D(QQmlEngine);
- QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType);
+ QQmlType type = QQmlMetaType::qmlTypeById(qmlTypeId);
if (!type.isValid() || !type.isSingleton())
return QJSValue();
@@ -1380,6 +1054,48 @@ QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
return d->singletonInstance<QJSValue>(type);
}
+
+/*!
+ \fn template<typename T> T QQmlEngine::singletonInstance(QAnyStringView uri, QAnyStringView typeName)
+
+ \overload
+ Returns the instance of a singleton type named \a typeName from the module specified by \a uri.
+
+ This method can be used as an alternative to calling qmlTypeId followed by the id based overload of
+ singletonInstance. This is convenient when one only needs to do a one time setup of a
+ singleton; if repeated access to the singleton is required, caching its typeId will allow
+ faster subsequent access via the
+ \l {QQmlEngine::singletonInstance(int qmlTypeId)}{type-id based overload}.
+
+ The template argument \e T may be either QJSValue or a pointer to a QObject-derived
+ type and depends on how the singleton was registered. If no instance of \e T has been
+ created yet, it is created now. If \a typeName does not represent a valid singleton
+ type, either a default constructed QJSValue or a \c nullptr is returned.
+
+ \snippet code/src_qml_qqmlengine.cpp 5
+
+ \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
+ \since 6.5
+*/
+template<>
+QJSValue QQmlEngine::singletonInstance<QJSValue>(QAnyStringView uri, QAnyStringView typeName)
+{
+ Q_D(QQmlEngine);
+
+ auto loadHelper = QQml::makeRefPointer<LoadHelper>(&d->typeLoader, uri);
+
+ auto [moduleStatus, type] = loadHelper->resolveType(typeName);
+
+ if (moduleStatus == LoadHelper::ResolveTypeResult::NoSuchModule)
+ return {};
+ if (!type.isValid())
+ return {};
+ if (!type.isSingleton())
+ return {};
+
+ return d->singletonInstance<QJSValue>(type);
+}
+
/*!
Refreshes all binding expressions that use strings marked for translation.
@@ -1387,31 +1103,23 @@ QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
QCoreApplication::installTranslator, to ensure that your user-interface
shows up-to-date translations.
- \note Due to a limitation in the implementation, this function
- refreshes all the engine's bindings, not only those that use strings
- marked for translation.
- This may be optimized in a future release.
-
\since 5.10
*/
void QQmlEngine::retranslate()
{
Q_D(QQmlEngine);
- QQmlContextData *context = QQmlContextData::get(d->rootContext)->childContexts;
- while (context) {
- context->refreshExpressions();
- context = context->nextChild;
- }
+ d->translationLanguage.notify();
}
/*!
- Returns the QQmlContext for the \a object, or 0 if no
+ Returns the QQmlContext for the \a object, or nullptr if no
context has been set.
- When the QQmlEngine instantiates a QObject, the context is
- set automatically.
+ When the QQmlEngine instantiates a QObject, an internal context is assigned
+ to it automatically. Such internal contexts are read-only. You cannot set
+ context properties on them.
- \sa qmlContext(), qmlEngine()
+ \sa qmlContext(), qmlEngine(), QQmlContext::setContextProperty()
*/
QQmlContext *QQmlEngine::contextForObject(const QObject *object)
{
@@ -1444,78 +1152,10 @@ void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context)
return;
}
- QQmlContextData *contextData = QQmlContextData::get(context);
+ QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
Q_ASSERT(data->context == nullptr);
- data->context = contextData;
- contextData->addObject(data);
-}
-
-/*!
- \enum QQmlEngine::ObjectOwnership
-
- ObjectOwnership controls whether or not QML automatically destroys the
- QObject when the corresponding JavaScript object is garbage collected by the
- engine. The two ownership options are:
-
- \value CppOwnership The object is owned by C++ code and QML will never delete
- it. The JavaScript destroy() method cannot be used on these objects. This
- option is similar to QScriptEngine::QtOwnership.
-
- \value JavaScriptOwnership The object is owned by JavaScript. When the object
- is returned to QML as the return value of a method call, QML will track it
- and delete it if there are no remaining JavaScript references to it and
- it has no QObject::parent(). An object tracked by one QQmlEngine will be
- deleted during that QQmlEngine's destructor. Thus, JavaScript references
- between objects with JavaScriptOwnership from two different engines will
- not be valid if one of these engines is deleted. This option is similar to
- QScriptEngine::ScriptOwnership.
-
- Generally an application doesn't need to set an object's ownership
- explicitly. QML uses a heuristic to set the default ownership. By default, an
- object that is created by QML has JavaScriptOwnership. The exception to this
- are the root objects created by calling QQmlComponent::create() or
- QQmlComponent::beginCreate(), which have CppOwnership by default. The
- ownership of these root-level objects is considered to have been transferred
- to the C++ caller.
-
- Objects not-created by QML have CppOwnership by default. The exception to this
- are objects returned from C++ method calls; their ownership will be set to
- JavaScriptOwnership. This applies only to explicit invocations of Q_INVOKABLE
- methods or slots, but not to property getter invocations.
-
- Calling setObjectOwnership() overrides the default ownership heuristic used by
- QML.
-*/
-
-/*!
- Sets the \a ownership of \a object.
-*/
-void QQmlEngine::setObjectOwnership(QObject *object, ObjectOwnership ownership)
-{
- if (!object)
- return;
-
- QQmlData *ddata = QQmlData::get(object, true);
- if (!ddata)
- return;
-
- ddata->indestructible = (ownership == CppOwnership)?true:false;
- ddata->explicitIndestructibleSet = true;
-}
-
-/*!
- Returns the ownership of \a object.
-*/
-QQmlEngine::ObjectOwnership QQmlEngine::objectOwnership(QObject *object)
-{
- if (!object)
- return CppOwnership;
-
- QQmlData *ddata = QQmlData::get(object, false);
- if (!ddata)
- return CppOwnership;
- else
- return ddata->indestructible?CppOwnership:JavaScriptOwnership;
+ data->context = contextData.data();
+ contextData->addOwnedObject(data);
}
/*!
@@ -1523,165 +1163,13 @@ QQmlEngine::ObjectOwnership QQmlEngine::objectOwnership(QObject *object)
*/
bool QQmlEngine::event(QEvent *e)
{
- Q_D(QQmlEngine);
- if (e->type() == QEvent::User)
- d->doDeleteInEngineThread();
- else if (e->type() == QEvent::LanguageChange) {
+ if (e->type() == QEvent::LanguageChange) {
retranslate();
}
return QJSEngine::event(e);
}
-void QQmlEnginePrivate::doDeleteInEngineThread()
-{
- QFieldList<Deletable, &Deletable::next> list;
- mutex.lock();
- list.copyAndClear(toDeleteInEngineThread);
- mutex.unlock();
-
- while (Deletable *d = list.takeFirst())
- delete d;
-}
-
-namespace QtQml {
-
-void qmlExecuteDeferred(QObject *object)
-{
- QQmlData *data = QQmlData::get(object);
-
- if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine);
-
- QQmlComponentPrivate::DeferredState state;
- QQmlComponentPrivate::beginDeferred(ep, object, &state);
-
- // Release the reference for the deferral action (we still have one from construction)
- data->releaseDeferredData();
-
- QQmlComponentPrivate::completeDeferred(ep, &state);
- }
-}
-
-QQmlContext *qmlContext(const QObject *obj)
-{
- return QQmlEngine::contextForObject(obj);
-}
-
-QQmlEngine *qmlEngine(const QObject *obj)
-{
- QQmlData *data = QQmlData::get(obj, false);
- if (!data || !data->context)
- return nullptr;
- return data->context->engine;
-}
-
-static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data,
- QObject *object, bool create)
-{
- if (!pf)
- return nullptr;
-
- QObject *rv = data->hasExtendedData() ? data->attachedProperties()->value(pf) : 0;
- if (rv || !create)
- return rv;
-
- rv = pf(object);
-
- if (rv)
- data->attachedProperties()->insert(pf, rv);
-
- return rv;
-}
-
-#if QT_DEPRECATED_SINCE(5, 14)
-QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create)
-{
- QQmlData *data = QQmlData::get(object, create);
-
- // Attached properties are only on objects created by QML,
- // unless explicitly requested (create==true)
- if (!data)
- return nullptr;
-
- QQmlEnginePrivate *engine = QQmlEnginePrivate::get(data->context);
- return resolveAttachedProperties(QQmlMetaType::attachedPropertiesFuncById(engine, id), data,
- const_cast<QObject *>(object), create);
-}
-
-QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object,
- const QMetaObject *attachedMetaObject, bool create)
-{
- if (*idCache == -1) {
- QQmlEngine *engine = object ? qmlEngine(object) : nullptr;
- *idCache = QQmlMetaType::attachedPropertiesFuncId(engine ? QQmlEnginePrivate::get(engine) : nullptr, attachedMetaObject);
- }
-
- if (*idCache == -1 || !object)
- return nullptr;
-
- return qmlAttachedPropertiesObjectById(*idCache, object, create);
-}
-#endif
-
-QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object,
- const QMetaObject *attachedMetaObject)
-{
- QQmlEngine *engine = object ? qmlEngine(object) : nullptr;
- return QQmlMetaType::attachedPropertiesFunc(engine ? QQmlEnginePrivate::get(engine) : nullptr,
- attachedMetaObject);
-}
-
-QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool create)
-{
- if (!object)
- return nullptr;
-
- QQmlData *data = QQmlData::get(object, create);
-
- // Attached properties are only on objects created by QML,
- // unless explicitly requested (create==true)
- if (!data)
- return nullptr;
-
- return resolveAttachedProperties(func, data, object, create);
-}
-
-} // namespace QtQml
-
-#if QT_DEPRECATED_SINCE(5, 1)
-
-// Also define symbols outside namespace to keep binary compatibility with Qt 5.0
-
-Q_QML_EXPORT void qmlExecuteDeferred(QObject *obj)
-{
- QtQml::qmlExecuteDeferred(obj);
-}
-
-Q_QML_EXPORT QQmlContext *qmlContext(const QObject *obj)
-{
- return QtQml::qmlContext(obj);
-}
-
-Q_QML_EXPORT QQmlEngine *qmlEngine(const QObject *obj)
-{
- return QtQml::qmlEngine(obj);
-}
-
-Q_QML_EXPORT QObject *qmlAttachedPropertiesObjectById(int id, const QObject *obj, bool create)
-{
- return QtQml::qmlAttachedPropertiesObjectById(id, obj, create);
-}
-
-Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object,
- const QMetaObject *attachedMetaObject,
- bool create)
-{
- return QtQml::qmlAttachedPropertiesObject(idCache, object, attachedMetaObject, create);
-}
-
-#endif // QT_DEPRECATED_SINCE(5, 1)
-
class QQmlDataExtended {
public:
QQmlDataExtended();
@@ -1751,7 +1239,9 @@ void QQmlData::NotifyList::layout()
todo = nullptr;
}
-void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *context)
+void QQmlData::deferData(
+ int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QQmlRefPointer<QQmlContextData> &context)
{
QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
deferData->deferredIdx = objectIndex;
@@ -1759,13 +1249,14 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCo
deferData->context = context;
const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex);
- const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex);
+ const QV4::CompiledData::BindingPropertyData *propertyData
+ = compilationUnit->bindingPropertyDataPerObjectAt(objectIndex);
const QV4::CompiledData::Binding *binding = compiledObject->bindingTable();
for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) {
- const QQmlPropertyData *property = propertyData.at(i);
- if (property && binding->flags & QV4::CompiledData::Binding::IsDeferredBinding)
- deferData->bindings.insert(property->coreIndex(), binding);
+ const QQmlPropertyData *property = propertyData->at(i);
+ if (binding->hasFlag(QV4::CompiledData::Binding::IsDeferredBinding))
+ deferData->bindings.insert(property ? property->coreIndex() : -1, binding);
}
deferredData.append(deferData);
@@ -1787,49 +1278,73 @@ void QQmlData::releaseDeferredData()
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
- if (!notifyList) {
- notifyList = (NotifyList *)malloc(sizeof(NotifyList));
- notifyList->connectionMask = 0;
- notifyList->maximumTodoIndex = 0;
- notifyList->notifiesSize = 0;
- notifyList->todo = nullptr;
- notifyList->notifies = nullptr;
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
+ NotifyList *list = notifyList.loadRelaxed();
+
+ if (!list) {
+ list = new NotifyList;
+ // We don't really care when this change takes effect on other threads. The notifyList can
+ // only become non-null once in the life time of a QQmlData. It becomes null again when the
+ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB
+ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays
+ // non-null "forever". We can apply relaxed semantics.
+ notifyList.storeRelaxed(list);
}
Q_ASSERT(!endpoint->isConnected());
index = qMin(index, 0xFFFF - 1);
- notifyList->connectionMask |= (1ULL << quint64(index % 64));
- if (index < notifyList->notifiesSize) {
+ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other
+ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying
+ // the conenctionMask in the presence of concurrent modification, any result is correct.
+ list->connectionMask.storeRelaxed(
+ list->connectionMask.loadRelaxed() | (1ULL << quint64(index % 64)));
- endpoint->next = notifyList->notifies[index];
+ if (index < list->notifiesSize) {
+ endpoint->next = list->notifies[index];
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->notifies[index];
- notifyList->notifies[index] = endpoint;
-
+ endpoint->prev = &list->notifies[index];
+ list->notifies[index] = endpoint;
} else {
- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index);
+ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index);
- endpoint->next = notifyList->todo;
+ endpoint->next = list->todo;
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->todo;
- notifyList->todo = endpoint;
+ endpoint->prev = &list->todo;
+ list->todo = endpoint;
}
}
-void QQmlData::disconnectNotifiers()
+void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete)
{
- if (notifyList) {
- while (notifyList->todo)
- notifyList->todo->disconnect();
- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) {
- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii])
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+ if (NotifyList *list = notifyList.loadRelaxed()) {
+ while (QQmlNotifierEndpoint *todo = list->todo)
+ todo->disconnect();
+ for (int ii = 0; ii < list->notifiesSize; ++ii) {
+ while (QQmlNotifierEndpoint *ep = list->notifies[ii])
ep->disconnect();
}
- free(notifyList->notifies);
- free(notifyList);
- notifyList = nullptr;
+ free(list->notifies);
+
+ if (doDelete == DeleteNotifyList::Yes) {
+ // We can only get here from QQmlData::destroyed(), and that can only come from the
+ // the QObject dtor. If you're still sending signals at that point you have UB already
+ // without any threads. Therefore, it's enough to apply relaxed semantics.
+ notifyList.storeRelaxed(nullptr);
+ delete list;
+ } else {
+ // We can use relaxed semantics here. The worst thing that can happen is that some
+ // signal is falsely reported as connected. Signal connectedness across threads
+ // is not quite deterministic anyway.
+ list->connectionMask.storeRelaxed(0);
+ list->maximumTodoIndex = 0;
+ list->notifiesSize = 0;
+ list->notifies = nullptr;
+
+ }
}
}
@@ -1845,8 +1360,8 @@ void QQmlData::destroyed(QObject *object)
nextContextObject->prevContextObject = prevContextObject;
if (prevContextObject)
*prevContextObject = nextContextObject;
- else if (outerContext && outerContext->contextObjects == this)
- outerContext->contextObjects = nextContextObject;
+ else if (outerContext && outerContext->ownedObjects() == this)
+ outerContext->setOwnedObjects(nextContextObject);
QQmlAbstractBinding *binding = bindings;
while (binding) {
@@ -1856,7 +1371,7 @@ void QQmlData::destroyed(QObject *object)
if (bindings && !bindings->ref.deref())
delete bindings;
- compilationUnit = nullptr;
+ compilationUnit.reset();
qDeleteAll(deferredData);
deferredData.clear();
@@ -1903,17 +1418,18 @@ void QQmlData::destroyed(QObject *object)
free(bindingBits);
if (propertyCache)
- propertyCache->release();
+ propertyCache.reset();
- ownContext = nullptr;
+ ownContext.reset();
while (guards) {
- QQmlGuard<QObject> *guard = static_cast<QQmlGuard<QObject> *>(guards);
- *guard = (QObject *)nullptr;
- guard->objectDestroyed(object);
+ auto *guard = guards;
+ guard->setObject(nullptr);
+ if (guard->objectDestroyed)
+ guard->objectDestroyed(guard);
}
- disconnectNotifiers();
+ disconnectNotifiers(DeleteNotifyList::Yes);
if (extendedData)
delete extendedData;
@@ -1927,25 +1443,6 @@ void QQmlData::destroyed(QObject *object)
this->~QQmlData();
}
-DEFINE_BOOL_CONFIG_OPTION(parentTest, QML_PARENT_TEST);
-
-void QQmlData::parentChanged(QObject *object, QObject *parent)
-{
- if (parentTest()) {
- if (parentFrozen && !QObjectPrivate::get(object)->wasDeleted) {
- QString on;
- QString pn;
-
- { QDebug dbg(&on); dbg << object; on = on.left(on.length() - 1); }
- { QDebug dbg(&pn); dbg << parent; pn = pn.left(pn.length() - 1); }
-
- qFatal("Object %s has had its parent frozen by QML and cannot be changed.\n"
- "User code is attempting to change it to %s.\n"
- "This behavior is NOT supported!", qPrintable(on), qPrintable(pn));
- }
- }
-}
-
QQmlData::BindingBitsType *QQmlData::growBits(QObject *obj, int bit)
{
BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
@@ -1973,16 +1470,14 @@ QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv)
{
Q_ASSERT(priv);
Q_ASSERT(!priv->isDeletingChildren);
- priv->declarativeData = new QQmlData;
+ priv->declarativeData = new QQmlData(OwnsMemory);
return static_cast<QQmlData *>(priv->declarativeData);
}
-QQmlPropertyCache *QQmlData::createPropertyCache(QJSEngine *engine, QObject *object)
+QQmlPropertyCache::ConstPtr QQmlData::createPropertyCache(QObject *object)
{
QQmlData *ddata = QQmlData::get(object, /*create*/true);
- ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object);
- if (ddata->propertyCache)
- ddata->propertyCache->addref();
+ ddata->propertyCache = QQmlMetaType::propertyCache(object, QTypeRevision {});
return ddata->propertyCache;
}
@@ -2008,38 +1503,38 @@ static void dumpwarning(const QQmlError &error)
switch (error.messageType()) {
case QtDebugMsg:
QMessageLogger(error.url().toString().toLatin1().constData(),
- error.line(), nullptr).debug().nospace()
- << qPrintable(error.toString());
+ error.line(), nullptr).debug().noquote().nospace()
+ << error.toString();
break;
case QtInfoMsg:
QMessageLogger(error.url().toString().toLatin1().constData(),
- error.line(), nullptr).info().nospace()
- << qPrintable(error.toString());
+ error.line(), nullptr).info().noquote().nospace()
+ << error.toString();
break;
case QtWarningMsg:
case QtFatalMsg: // fatal does not support streaming, and furthermore, is actually fatal. Probably not desirable for QML.
QMessageLogger(error.url().toString().toLatin1().constData(),
- error.line(), nullptr).warning().nospace()
- << qPrintable(error.toString());
+ error.line(), nullptr).warning().noquote().nospace()
+ << error.toString();
break;
case QtCriticalMsg:
QMessageLogger(error.url().toString().toLatin1().constData(),
- error.line(), nullptr).critical().nospace()
- << qPrintable(error.toString());
+ error.line(), nullptr).critical().noquote().nospace()
+ << error.toString();
break;
}
}
static void dumpwarning(const QList<QQmlError> &errors)
{
- for (int ii = 0; ii < errors.count(); ++ii)
+ for (int ii = 0; ii < errors.size(); ++ii)
dumpwarning(errors.at(ii));
}
void QQmlEnginePrivate::warning(const QQmlError &error)
{
Q_Q(QQmlEngine);
- q->warnings(QList<QQmlError>() << error);
+ emit q->warnings(QList<QQmlError>({error}));
if (outputWarningsToMsgLog)
dumpwarning(error);
}
@@ -2047,7 +1542,7 @@ void QQmlEnginePrivate::warning(const QQmlError &error)
void QQmlEnginePrivate::warning(const QList<QQmlError> &errors)
{
Q_Q(QQmlEngine);
- q->warnings(errors);
+ emit q->warnings(errors);
if (outputWarningsToMsgLog)
dumpwarning(errors);
}
@@ -2090,15 +1585,15 @@ QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(
QList<QQmlError> errors;
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
if (m.isWarning()) {
- qWarning("%s:%d : %s", qPrintable(fileName), m.line, qPrintable(m.message));
+ qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
continue;
}
QQmlError error;
error.setUrl(QUrl(fileName));
error.setDescription(m.message);
- error.setLine(m.line);
- error.setColumn(m.column);
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(m.loc.startLine));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(m.loc.startColumn));
errors << error;
}
return errors;
@@ -2130,7 +1625,8 @@ void QQmlEnginePrivate::cleanupScarceResources()
The newly added \a path will be first in the importPathList().
- \sa setImportPathList(), {QML Modules}
+ \b {See also} \l setImportPathList(), \l {QML Modules},
+ and \l [QtQml] {QML Import Path}
*/
void QQmlEngine::addImportPath(const QString& path)
{
@@ -2148,9 +1644,8 @@ void QQmlEngine::addImportPath(const QString& path)
provided by that module. A \c qmldir file is required for defining the
type version mapping and possibly QML extensions plugins.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML2_IMPORT_PATH environment variable,
- and the builtin \c Qml2ImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
\sa addImportPath(), setImportPathList()
*/
@@ -2164,9 +1659,11 @@ QStringList QQmlEngine::importPathList() const
Sets \a paths as the list of directories where the engine searches for
installed modules in a URL-based directory structure.
- By default, the list contains the directory of the application executable,
- paths specified in the \c QML2_IMPORT_PATH environment variable,
- and the builtin \c Qml2ImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
+
+ \warning Calling setImportPathList does not preserve the default
+ import paths.
\sa importPathList(), addImportPath()
*/
@@ -2194,7 +1691,6 @@ void QQmlEngine::addPluginPath(const QString& path)
d->importDatabase.addPluginPath(path);
}
-
/*!
Returns the list of directories where the engine searches for
native plugins for imported modules (referenced in the \c qmldir file).
@@ -2227,20 +1723,31 @@ void QQmlEngine::setPluginPathList(const QStringList &paths)
}
#if QT_CONFIG(library)
+#if QT_DEPRECATED_SINCE(6, 4)
/*!
+ \deprecated [6.4] Import the module from QML with an "import" statement instead.
+
Imports the plugin named \a filePath with the \a uri provided.
Returns true if the plugin was successfully imported; otherwise returns false.
On failure and if non-null, the \a errors list will have any errors which occurred prepended to it.
The plugin has to be a Qt plugin which implements the QQmlEngineExtensionPlugin interface.
+
+ \note Directly loading plugins like this can confuse the module import logic. In order to make
+ the import logic load plugins from a specific place, you can use \l addPluginPath(). Each
+ plugin should be part of a QML module that you can import using the "import" statement.
*/
bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
{
Q_D(QQmlEngine);
- return d->importDatabase.importDynamicPlugin(filePath, uri, QString(), -1, errors);
+ QQmlTypeLoaderQmldirContent qmldir;
+ QQmlPluginImporter importer(
+ uri, QTypeRevision(), &d->importDatabase, &qmldir, &d->typeLoader, errors);
+ return importer.importDynamicPlugin(filePath, uri, false).isValid();
}
#endif
+#endif
/*!
\property QQmlEngine::offlineStoragePath
@@ -2249,7 +1756,7 @@ bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList
Returns the directory where SQL and other offline
storage is placed.
- The SQL databases created with openDatabase() are stored here.
+ The SQL databases created with \c openDatabaseSync() are stored here.
The default is QML/OfflineStorage in the platform-standard
user application data directory.
@@ -2257,11 +1764,23 @@ bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList
Note that the path may not currently exist on the filesystem, so
callers wanting to \e create new files at this location should create
it first - see QDir::mkpath().
+
+ \sa {Qt Quick Local Storage QML Types}
*/
+
+/*!
+ \fn void QQmlEngine::offlineStoragePathChanged()
+ This signal is emitted when \l offlineStoragePath changes.
+ \since 6.5
+*/
+
void QQmlEngine::setOfflineStoragePath(const QString& dir)
{
Q_D(QQmlEngine);
+ if (dir == d->offlineStoragePath)
+ return;
d->offlineStoragePath = dir;
+ Q_EMIT offlineStoragePathChanged();
}
QString QQmlEngine::offlineStoragePath() const
@@ -2269,12 +1788,14 @@ QString QQmlEngine::offlineStoragePath() const
Q_D(const QQmlEngine);
if (d->offlineStoragePath.isEmpty()) {
- QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+ QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QQmlEnginePrivate *e = const_cast<QQmlEnginePrivate *>(d);
- if (!dataLocation.isEmpty())
+ if (!dataLocation.isEmpty()) {
e->offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator())
+ QDir::separator() + QLatin1String("QML")
+ QDir::separator() + QLatin1String("OfflineStorage");
+ Q_EMIT e->q_func()->offlineStoragePathChanged();
+ }
}
return d->offlineStoragePath;
@@ -2295,144 +1816,24 @@ QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName)
return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex());
}
-// #### Qt 6: Remove this function, it exists only for binary compatibility.
-/*!
- * \internal
- */
-bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName)
-{
- Q_UNUSED(name)
- Q_UNUSED(fileName)
- return false;
-}
-
QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
{
Q_Q(const QQmlEngine);
return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
}
-bool QQmlEnginePrivate::isQObject(int t)
-{
- Locker locker(this);
- return m_compositeTypes.contains(t) || QQmlMetaType::isQObject(t);
-}
-
-QObject *QQmlEnginePrivate::toQObject(const QVariant &v, bool *ok) const
-{
- Locker locker(this);
- int t = v.userType();
- if (t == QMetaType::QObjectStar || m_compositeTypes.contains(t)) {
- if (ok) *ok = true;
- return *(QObject *const *)(v.constData());
- } else {
- return QQmlMetaType::toQObject(v, ok);
- }
-}
-
-QQmlMetaType::TypeCategory QQmlEnginePrivate::typeCategory(int t) const
-{
- Locker locker(this);
- if (m_compositeTypes.contains(t))
- return QQmlMetaType::Object;
- return QQmlMetaType::typeCategory(t);
-}
-
-bool QQmlEnginePrivate::isList(int t) const
-{
- return QQmlMetaType::isList(t);
-}
-
-int QQmlEnginePrivate::listType(int t) const
-{
- return QQmlMetaType::listType(t);
-}
-
-QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
-{
- Locker locker(this);
- auto iter = m_compositeTypes.constFind(t);
- if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache().data());
- } else {
- QQmlType type = QQmlMetaType::qmlType(t);
- return QQmlMetaObject(type.baseMetaObject());
- }
-}
-
-QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
-{
- Locker locker(this);
- auto iter = m_compositeTypes.constFind(t);
- if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache().data());
- } else {
- QQmlType type = QQmlMetaType::qmlType(t);
- return QQmlMetaObject(type.metaObject());
- }
-}
-
-QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
-{
- Locker locker(this);
- auto iter = m_compositeTypes.constFind(t);
- if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache().data();
- } else {
- QQmlType type = QQmlMetaType::qmlType(t);
- locker.unlock();
- return type.isValid() ? cache(type.metaObject()) : nullptr;
- }
-}
-
-QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVersion)
-{
- Locker locker(this);
- auto iter = m_compositeTypes.constFind(t);
- if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache().data();
- } else {
- QQmlType type = QQmlMetaType::qmlType(t);
- locker.unlock();
-
- if (minorVersion >= 0)
- return type.isValid() ? cache(type, minorVersion) : nullptr;
- else
- return type.isValid() ? cache(type.baseMetaObject()) : nullptr;
- }
-}
-
-void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
-{
- compilationUnit->isRegisteredWithEngine = true;
-
- Locker locker(this);
- // The QQmlCompiledData is not referenced here, but it is removed from this
- // hash in the QQmlCompiledData destructor
- m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit);
-}
-
-void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
-{
- compilationUnit->isRegisteredWithEngine = false;
-
- Locker locker(this);
- m_compositeTypes.remove(compilationUnit->metaTypeId);
-}
-
template<>
QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
{
Q_Q(QQmlEngine);
- QJSValue value = singletonInstances.value(type);
- if (!value.isUndefined()) {
- return value;
- }
-
- QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
+ QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
Q_ASSERT(siinfo != nullptr);
+ QJSValue value = singletonInstances.value(siinfo);
+ if (!value.isUndefined())
+ return value;
+
if (siinfo->scriptCallback) {
value = siinfo->scriptCallback(q, q);
if (value.isQObject()) {
@@ -2441,7 +1842,7 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
// should behave identically to QML singleton types.
q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
}
- singletonInstances.insert(type, value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (siinfo->qobjectCallback) {
QObject *o = siinfo->qobjectCallback(q, q);
@@ -2452,49 +1853,160 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
qPrintable(QString::fromUtf8(type.typeName()))));
warning(error);
} else {
+ type.createProxy(o);
+
// if this object can use a property cache, create it now
- QQmlData::ensurePropertyCache(q, o);
+ QQmlData::ensurePropertyCache(o);
+
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types. You can, however, manually
+ // assign a context; and clearSingletons() retains the contexts, in which case
+ // we don't want to see warnings about the object already having a context.
+ QQmlData *data = QQmlData::get(o, true);
+ if (!data->context) {
+ auto contextData = QQmlContextData::get(new QQmlContext(q->rootContext(), q));
+ data->context = contextData.data();
+ contextData->addOwnedObject(data);
+ }
}
- // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
- // should behave identically to QML singleton types.
- q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
+
value = q->newQObject(o);
- singletonInstances.insert(type, value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (!siinfo->url.isEmpty()) {
QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
+ if (component.isError()) {
+ warning(component.errors());
+ v4engine()->throwError(QLatin1String("Due to the preceding error(s), Singleton \"%1\" could not be loaded.").arg(QString::fromUtf8(type.typeName())));
+
+ return QJSValue(QJSValue::UndefinedValue);
+ }
QObject *o = component.beginCreate(q->rootContext());
value = q->newQObject(o);
- singletonInstances.insert(type, value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
component.completeCreate();
}
return value;
}
-void QQmlEnginePrivate::destroySingletonInstance(const QQmlType &type)
+bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const
{
- Q_ASSERT(type.isSingleton() || type.isCompositeSingleton());
+ return typeLoader.isTypeLoaded(url);
+}
- QObject* o = singletonInstances.take(type).toQObject();
- if (o) {
- QQmlData *ddata = QQmlData::get(o, false);
- if (type.singletonInstanceInfo()->url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet)
- return;
- delete o;
+bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const
+{
+ return typeLoader.isScriptLoaded(url);
+}
+
+void QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex,
+ QObject *thisObject, int argc, void **args,
+ QMetaType *types)
+{
+ const auto unit = compilationUnitFromUrl(url);
+ if (!unit)
+ return;
+ executeRuntimeFunction(unit, functionIndex, thisObject, argc, args, types);
+}
+
+void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit,
+ qsizetype functionIndex, QObject *thisObject,
+ int argc, void **args, QMetaType *types)
+{
+ Q_ASSERT(unit);
+ Q_ASSERT((functionIndex >= 0) && (functionIndex < unit->runtimeFunctions.size()));
+ Q_ASSERT(thisObject);
+
+ QQmlData *ddata = QQmlData::get(thisObject);
+ Q_ASSERT(ddata && ddata->outerContext);
+
+ QV4::Function *function = unit->runtimeFunctions[functionIndex];
+ Q_ASSERT(function);
+ Q_ASSERT(function->compiledFunction);
+
+ QV4::ExecutionEngine *v4 = v4engine();
+
+ // NB: always use scriptContext() by default as this method ignores whether
+ // there's already a stack frame (except when dealing with closures). the
+ // method is called from C++ (through QQmlEngine::executeRuntimeFunction())
+ // and thus the caller must ensure correct setup
+ QV4::Scope scope(v4);
+ QV4::ExecutionContext *ctx = v4->scriptContext();
+ QV4::Scoped<QV4::ExecutionContext> callContext(scope,
+ QV4::QmlContext::create(ctx, ddata->outerContext, thisObject));
+
+ if (auto nested = function->nestedFunction()) {
+ // if a nested function is already known, call the closure directly
+ function = nested;
+ } else if (function->isClosureWrapper()) {
+ // if there is a nested function, but we don't know it, we need to call
+ // an outer function first and then the inner function. we fetch the
+ // return value of a function call (that is a closure) by calling a
+ // different version of ExecutionEngine::callInContext() that returns a
+ // QV4::ReturnedValue with no arguments since they are not needed by the
+ // outer function anyhow
+ QV4::Scoped<QV4::JavaScriptFunctionObject> result(scope,
+ v4->callInContext(function, thisObject, callContext, 0, nullptr));
+ Q_ASSERT(result->function());
+ Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
+
+ // overwrite the function and its context
+ function = result->function();
+ callContext = QV4::Scoped<QV4::ExecutionContext>(scope, result->scope());
}
+
+ v4->callInContext(function, thisObject, callContext, argc, args, types);
}
-bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const
+QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)
{
- return typeLoader.isTypeLoaded(url);
+ QV4::ExecutionEngine *v4 = v4engine();
+ if (auto unit = v4->compilationUnitForUrl(url)) {
+ if (!unit->runtimeStrings)
+ unit->populate();
+ return unit.data();
+ }
+
+ auto unit = typeLoader.getType(url)->compilationUnit();
+ if (!unit)
+ return nullptr;
+
+ auto executable = v4->executableCompilationUnit(std::move(unit));
+ executable->populate();
+ return executable.data();
}
-bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const
+QQmlRefPointer<QQmlContextData>
+QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlRefPointer<QQmlContextData> &parentContext,
+ int subComponentIndex, bool isComponentRoot)
{
- return typeLoader.isScriptLoaded(url);
+ Q_ASSERT(unit);
+
+ QQmlRefPointer<QQmlContextData> context;
+ context = QQmlContextData::createRefCounted(parentContext);
+ context->setInternal(true);
+ context->setImports(unit->typeNameCache());
+ context->initFromTypeCompilationUnit(unit, subComponentIndex);
+
+ const auto *dependentScripts = unit->dependentScriptsPtr();
+ const qsizetype dependentScriptsSize = dependentScripts->size();
+ if (isComponentRoot && dependentScriptsSize) {
+ QV4::ExecutionEngine *v4 = v4engine();
+ Q_ASSERT(v4);
+ QV4::Scope scope(v4);
+
+ QV4::ScopedObject scripts(scope, v4->newArrayObject(dependentScriptsSize));
+ context->setImportedScripts(QV4::PersistentValue(v4, scripts.asReturnedValue()));
+ QV4::ScopedValue v(scope);
+ for (qsizetype i = 0; i < dependentScriptsSize; ++i)
+ scripts->put(i, (v = dependentScripts->at(i)->scriptValueForContext(context)));
+ }
+
+ return context;
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN)
// Normalize a file name using Shell API. As opposed to converting it
// to a short 8.3 name and back, this also works for drives where 8.3 notation
// is disabled (see 8dot3name options of fsutil.exe).
@@ -2526,7 +2038,7 @@ static inline QString shellNormalizeFileName(const QString &name)
canonicalName[0] = canonicalName.at(0).toUpper();
return QDir::cleanPath(canonicalName);
}
-#endif // Q_OS_WIN && !Q_OS_WINRT
+#endif // Q_OS_WIN
bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
{
@@ -2534,7 +2046,7 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
QFileInfo info(fileName);
const QString absolute = info.absoluteFilePath();
-#if defined(Q_OS_DARWIN) || defined(Q_OS_WINRT)
+#if defined(Q_OS_DARWIN)
const QString canonical = info.canonicalFilePath();
#elif defined(Q_OS_WIN)
// No difference if the path is qrc based
@@ -2571,8 +2083,8 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
return false;
}
#else
- Q_UNUSED(lengthIn)
- Q_UNUSED(fileName)
+ Q_UNUSED(lengthIn);
+ Q_UNUSED(fileName);
#endif
return true;
}
@@ -2601,6 +2113,53 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
\sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine()
*/
+void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
+
+LoadHelper::LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri)
+ : QQmlTypeLoader::Blob({}, QQmlDataBlob::QmlFile, loader)
+ , m_uri(uri.toString())
+
+{
+ auto import = std::make_shared<PendingImport>();
+ import->uri = m_uri;
+ QList<QQmlError> errorList;
+ if (!Blob::addImport(import, &errorList)) {
+ qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList;
+ m_uri.clear(); // reset m_uri to remember the failure
+ }
+}
+
+LoadHelper::ResolveTypeResult LoadHelper::resolveType(QAnyStringView typeName)
+{
+ QQmlType type;
+ if (!couldFindModule())
+ return {ResolveTypeResult::NoSuchModule, type};
+ QQmlTypeModule *module = QQmlMetaType::typeModule(m_uri, QTypeRevision{});
+ if (module) {
+ type = module->type(typeName.toString(), {});
+ if (type.isValid())
+ return {ResolveTypeResult::ModuleFound, type};
+ }
+ // The module exists (see check above), but there is no QQmlTypeModule
+ // ==> pure QML module, attempt resolveType
+ QTypeRevision versionReturn;
+ QList<QQmlError> errors;
+ QQmlImportNamespace *ns_return = nullptr;
+ m_importCache->resolveType(
+ typeLoader(), typeName.toString(), &type, &versionReturn, &ns_return, &errors);
+ return {ResolveTypeResult::ModuleFound, type};
+}
+
+bool LoadHelper::couldFindModule() const
+{
+ if (m_uri.isEmpty())
+ return false;
+ for (const auto &import: std::as_const(m_unresolvedImports))
+ if (import->priority == 0) // compare QQmlTypeData::allDependenciesDone
+ return false;
+ return true;
+}
+
QT_END_NAMESPACE
#include "moc_qqmlengine.cpp"