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.cpp1319
1 files changed, 545 insertions, 774 deletions
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 30f24a8091..c7812059a1 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -1,67 +1,23 @@
-/****************************************************************************
-**
-** 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 "qqmlsourcecoordinate_p.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/qmetaobject.h>
#include <QDebug>
@@ -71,6 +27,10 @@
#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"
@@ -89,10 +49,7 @@
#endif
#include <private/qqmlplatform_p.h>
#include <private/qqmlloggingcategory_p.h>
-
-#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
-#endif
#ifdef Q_OS_WIN // for %APPDATA%
# include <qt_windows.h>
@@ -105,6 +62,8 @@
QT_BEGIN_NAMESPACE
+void qml_register_types_QML();
+
/*!
\qmltype QtObject
\instantiates QObject
@@ -161,7 +120,7 @@ QT_BEGIN_NAMESPACE
\endcode
*/
-bool QQmlEnginePrivate::qml_debugging_enabled = false;
+Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
bool QQmlEnginePrivate::s_designerMode = false;
bool QQmlEnginePrivate::designerMode()
@@ -238,426 +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 "wasm" - WebAssembly
- \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, {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),
- 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
- 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);
- 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;
#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 (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext().data(); lc;
- lc = lc->linkedContext()) {
- lc->invalidate();
- if (lc->contextObject() == o)
- lc->setContextObject(nullptr);
- }
- d->ownContext->invalidate();
- if (d->ownContext->contextObject() == o)
- d->ownContext->setContextObject(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->setContextObject(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()
- : 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),
- 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();
@@ -707,7 +304,10 @@ 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)
+
+ // 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();
@@ -718,14 +318,14 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
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();
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(QMetaType::VoidStar);
@@ -745,7 +345,7 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
QQmlThreadNotifierProxyObject *mpo = new QQmlThreadNotifierProxyObject;
mpo->target = object;
mpo->moveToThread(objectThreadData->thread.loadAcquire());
- QCoreApplication::postEvent(mpo, ev.take());
+ QCoreApplication::postEvent(mpo, ev.release());
} else {
QQmlNotifierEndpoint *ep = ddata->notify(index);
@@ -781,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);
}
}
@@ -795,39 +399,69 @@ void QQmlData::setQueuedForDeletion(QObject *object)
if (QQmlData *ddata = QQmlData::get(object)) {
if (ddata->ownContext) {
Q_ASSERT(ddata->ownContext.data() == ddata->context);
- ddata->context->emitDestruction();
- if (ddata->ownContext->contextObject() == object)
- ddata->ownContext->setContextObject(nullptr);
- ddata->ownContext = nullptr;
+ 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;
@@ -836,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);
@@ -864,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.
-
- 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());
-
- //add item to view, etc
- ...
- \endcode
+ 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.
- In this case, the Text item will be created in the engine's
- \l {QQmlEngine::rootContext()}{root context}.
+ 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}.
- \sa QQmlComponent, QQmlContext, {QML Global Object}
+ \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
*/
/*!
@@ -917,11 +540,12 @@ 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);
// Emit onDestruction signals for the root context before
@@ -933,9 +557,7 @@ QQmlEngine::~QQmlEngine()
// 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?
- const QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes();
- for (const QQmlType &currType : singletonTypes)
- d->destroySingletonInstance(currType);
+ d->singletonInstances.clear();
delete d->rootContext;
d->rootContext = nullptr;
@@ -977,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();
}
/*!
@@ -1002,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.
@@ -1080,20 +745,19 @@ QUrl QQmlEngine::interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataT
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);
}
@@ -1188,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));
}
@@ -1201,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();
}
@@ -1214,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);
}
@@ -1279,6 +943,45 @@ 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
@@ -1343,7 +1046,7 @@ 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();
@@ -1351,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.
@@ -1358,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);
- for (QQmlRefPointer<QQmlContextData> context
- = QQmlContextData::get(d->rootContext)->childContexts();
- context; context = context->nextChild()) {
- context->refreshExpressions();
- }
+ 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)
{
@@ -1426,27 +1163,13 @@ void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context)
*/
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;
-}
-
class QQmlDataExtended {
public:
QQmlDataExtended();
@@ -1526,13 +1249,14 @@ void QQmlData::deferData(
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);
@@ -1554,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;
+
+ }
}
}
@@ -1623,7 +1371,7 @@ void QQmlData::destroyed(QObject *object)
if (bindings && !bindings->ref.deref())
delete bindings;
- compilationUnit = nullptr;
+ compilationUnit.reset();
qDeleteAll(deferredData);
deferredData.clear();
@@ -1670,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;
@@ -1721,14 +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, QTypeRevision {}, true);
+ ddata->propertyCache = QQmlMetaType::propertyCache(object, QTypeRevision {});
return ddata->propertyCache;
}
@@ -1778,14 +1527,14 @@ static void dumpwarning(const QQmlError &error)
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);
}
@@ -1793,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);
}
@@ -1876,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)
{
@@ -1894,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 QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath from QLibraryInfo.
+ By default, this list contains the paths mentioned in
+ \l {QML Import Path}.
\sa addImportPath(), setImportPathList()
*/
@@ -1910,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 QML_IMPORT_PATH environment variable,
- and the builtin \c QmlImportsPath 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()
*/
@@ -1972,21 +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(), QTypeRevision(), false,
- errors).isValid();
+ QQmlTypeLoaderQmldirContent qmldir;
+ QQmlPluginImporter importer(
+ uri, QTypeRevision(), &d->importDatabase, &qmldir, &d->typeLoader, errors);
+ return importer.importDynamicPlugin(filePath, uri, false).isValid();
}
#endif
+#endif
/*!
\property QQmlEngine::offlineStoragePath
@@ -1995,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.
@@ -2003,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
@@ -2017,10 +1790,12 @@ QString QQmlEngine::offlineStoragePath() const
if (d->offlineStoragePath.isEmpty()) {
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;
@@ -2047,148 +1822,18 @@ QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
}
-static QQmlPropertyCache *propertyCacheForPotentialInlineComponentType(int t, const QHash<int, QV4::ExecutableCompilationUnit *>::const_iterator &iter) {
- if (t != (*iter)->typeIds.id.id()) {
- // this is an inline component, and what we have in the iterator is currently the parent compilation unit
- for (auto &&icDatum: (*iter)->inlineComponentData)
- if (icDatum.typeIds.id.id() == t)
- return (*iter)->propertyCaches.at(icDatum.objectIndex);
- }
- return (*iter)->rootPropertyCache().data();
-}
-
-/*!
- * \internal
- *
- * Look up by type's baseMetaObject.
- */
-QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
-{
- if (QQmlPropertyCache *composite = findPropertyCacheInCompositeTypes(t))
- return QQmlMetaObject(composite);
-
- QQmlType type = QQmlMetaType::qmlType(t);
- return QQmlMetaObject(type.baseMetaObject());
-}
-
-/*!
- * \internal
- *
- * Look up by type's metaObject.
- */
-QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
-{
- if (QQmlPropertyCache *composite = findPropertyCacheInCompositeTypes(t))
- return QQmlMetaObject(composite);
-
- QQmlType type = QQmlMetaType::qmlType(t);
- return QQmlMetaObject(type.metaObject());
-}
-
-/*!
- * \internal
- *
- * Look up by type's metaObject and version.
- */
-QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
-{
- if (QQmlPropertyCache *composite = findPropertyCacheInCompositeTypes(t))
- return composite;
-
- QQmlType type = QQmlMetaType::qmlType(t);
- return type.isValid() ? cache(type.metaObject(), type.version()) : nullptr;
-}
-
-/*!
- * \internal
- *
- * Look up by type's baseMetaObject and unspecified/any version.
- * TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or
- * the actual type's version seems strange. The behavior has been in place for a while.
- */
-QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t)
-{
- if (QQmlPropertyCache *composite = findPropertyCacheInCompositeTypes(t))
- return composite;
-
- QQmlType type = QQmlMetaType::qmlType(t);
- return type.isValid() ? cache(type.baseMetaObject(), QTypeRevision()) : nullptr;
-}
-
-/*!
- * \internal
- *
- * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type
- * has no revisiononed attributes here. Unspecified versions are interpreted as "any".
- */
-QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, QTypeRevision version)
-{
- if (QQmlPropertyCache *composite = findPropertyCacheInCompositeTypes(t))
- return composite;
-
- QQmlType type = QQmlMetaType::qmlType(t);
- if (!type.isValid())
- return nullptr;
-
- if (type.containsRevisionedAttributes())
- return QQmlMetaType::propertyCache(type, version);
-
- if (const QMetaObject *metaObject = type.metaObject())
- return cache(metaObject, version);
-
- return nullptr;
-}
-
-QQmlPropertyCache *QQmlEnginePrivate::findPropertyCacheInCompositeTypes(int t) const
-{
- Locker locker(this);
- auto iter = m_compositeTypes.constFind(t);
- return (iter == m_compositeTypes.constEnd())
- ? nullptr
- : propertyCacheForPotentialInlineComponentType(t, iter);
-}
-
-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->typeIds.id.id(), compilationUnit);
- for (auto &&data: compilationUnit->inlineComponentData)
- m_compositeTypes.insert(data.typeIds.id.id(), compilationUnit);
-}
-
-void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
-{
- compilationUnit->isRegisteredWithEngine = false;
-
- Locker locker(this);
- m_compositeTypes.remove(compilationUnit->typeIds.id.id());
- for (auto&& icDatum: compilationUnit->inlineComponentData)
- m_compositeTypes.remove(icDatum.typeIds.id.id());
-}
-
-QV4::ExecutableCompilationUnit *QQmlEnginePrivate::obtainExecutableCompilationUnit(int typeId)
-{
- Locker locker(this);
- return m_compositeTypes.value(typeId, nullptr);
-}
-
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()) {
@@ -2197,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.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (siinfo->qobjectCallback) {
QObject *o = siinfo->qobjectCallback(q, q);
@@ -2211,13 +1856,22 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
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.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
} else if (!siinfo->url.isEmpty()) {
QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
if (component.isError()) {
@@ -2228,26 +1882,13 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
}
QObject *o = component.beginCreate(q->rootContext());
value = q->newQObject(o);
- singletonInstances.convertAndInsert(v4engine(), type, &value);
+ singletonInstances.convertAndInsert(v4engine(), siinfo, &value);
component.completeCreate();
}
return value;
}
-void QQmlEnginePrivate::destroySingletonInstance(const QQmlType &type)
-{
- Q_ASSERT(type.isSingleton() || type.isCompositeSingleton());
-
- 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::isTypeLoaded(const QUrl &url) const
{
return typeLoader.isTypeLoaded(url);
@@ -2258,28 +1899,111 @@ bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const
return typeLoader.isScriptLoaded(url);
}
-QJSValue QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex,
- QObject *thisObject, int argc, void **args, QMetaType *types)
+void QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex,
+ QObject *thisObject, int argc, void **args,
+ QMetaType *types)
{
- Q_Q(QQmlEngine);
- if (const auto unit = typeLoader.getType(url)->compilationUnit()) {
- Q_ASSERT(functionIndex >= 0);
- Q_ASSERT(thisObject);
-
- if (!unit->engine)
- unit->linkToEngine(q->handle());
-
- if (unit->runtimeFunctions.length() <= functionIndex)
- return QJSValue();
-
- QQmlContext *ctx = q->contextForObject(thisObject);
- if (!ctx)
- ctx = q->rootContext();
- return QJSValuePrivate::fromReturnedValue(
- q->handle()->callInContext(unit->runtimeFunctions[functionIndex], thisObject,
- QQmlContextData::get(ctx), argc, args, 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::ScopedFunctionObject 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());
}
- return QJSValue();
+
+ v4->callInContext(function, thisObject, callContext, argc, args, types);
+}
+
+QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &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();
+}
+
+QQmlRefPointer<QQmlContextData>
+QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
+ const QQmlRefPointer<QQmlContextData> &parentContext,
+ int subComponentIndex, bool isComponentRoot)
+{
+ 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)
@@ -2389,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"