summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp15
-rw-r--r--src/corelib/kernel/qcoreapplication.h2
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h2
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp6
-rw-r--r--src/corelib/kernel/qfunctions_winrt_p.h6
-rw-r--r--src/corelib/kernel/qjni.cpp4
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp42
-rw-r--r--src/corelib/kernel/qjnienvironment.h10
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp22
-rw-r--r--src/corelib/kernel/qjniobject.cpp172
-rw-r--r--src/corelib/kernel/qmetaobject.cpp27
-rw-r--r--src/corelib/kernel/qmetaobject_p.h3
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp277
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h7
-rw-r--r--src/corelib/kernel/qmetatype.cpp20
-rw-r--r--src/corelib/kernel/qmetatype.h10
-rw-r--r--src/corelib/kernel/qobject.cpp4
-rw-r--r--src/corelib/kernel/qobject_p.h1
-rw-r--r--src/corelib/kernel/qobjectdefs.h2
-rw-r--r--src/corelib/kernel/qproperty.cpp644
-rw-r--r--src/corelib/kernel/qproperty.h62
-rw-r--r--src/corelib/kernel/qproperty_p.h179
-rw-r--r--src/corelib/kernel/qpropertyprivate.h69
-rw-r--r--src/corelib/kernel/qtimer.cpp20
-rw-r--r--src/corelib/kernel/qtimerinfo_unix.cpp7
-rw-r--r--src/corelib/kernel/qvariant.h2
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp70
-rw-r--r--src/corelib/kernel/qwineventnotifier_p.h15
28 files changed, 994 insertions, 706 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index ccdece6357..5ea01ec53d 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -1399,6 +1399,12 @@ void QCoreApplicationPrivate::execCleanup()
function \e does return to the caller -- it is event processing that
stops.
+ Note also that this function is not thread-safe. It should be called only
+ from the main thread (the thread that the QCoreApplication object is
+ processing events on). To ask the application to exit from another thread,
+ either use QCoreApplication::quit() or instead call this function from the
+ main thread with QMetaMethod::invokeMethod().
+
\sa quit(), exec()
*/
void QCoreApplication::exit(int returnCode)
@@ -1950,6 +1956,8 @@ void QCoreApplicationPrivate::maybeQuit()
}
/*!
+ \threadsafe
+
Asks the application to quit.
The request may be ignored if the application prevents the quit,
@@ -1961,7 +1969,7 @@ void QCoreApplicationPrivate::maybeQuit()
code 0 (success).
To exit the application without a chance of being interrupted, call
- exit() directly.
+ exit() directly. Note that method is not thread-safe.
It's good practice to always connect signals to this slot using a
\l{Qt::}{QueuedConnection}. If a signal connected (non-queued) to this slot
@@ -1974,6 +1982,11 @@ void QCoreApplicationPrivate::maybeQuit()
\snippet code/src_corelib_kernel_qcoreapplication.cpp 1
+ \b{Thread-safety note}: this function may be called from any thread to
+ thread-safely cause the currently-running main application loop to exit.
+ However, thread-safety is not guaranteed if the QCoreApplication object is
+ being destroyed at the same time.
+
\sa exit(), aboutToQuit()
*/
void QCoreApplication::quit()
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index 6539441180..f581d9b85c 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -120,7 +120,6 @@ public:
static int exec();
static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime);
- static void exit(int retcode = 0);
static bool sendEvent(QObject *receiver, QEvent *event);
static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);
@@ -165,6 +164,7 @@ public:
public Q_SLOTS:
static void quit();
+ static void exit(int retcode = 0);
Q_SIGNALS:
void aboutToQuit(QPrivateSignal);
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index 7b60ef5698..a888c4310f 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -198,7 +198,7 @@ public:
void processCommandLineArguments();
QString qmljs_debug_arguments; // a string containing arguments for js/qml debugging.
- inline QString qmljsDebugArgumentsString() { return qmljs_debug_arguments; }
+ inline QString qmljsDebugArgumentsString() const { return qmljs_debug_arguments; }
#ifdef QT_NO_QOBJECT
QCoreApplication *q_ptr;
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 19604de32f..80297feef1 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -475,13 +475,17 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
- d->interrupt.storeRelaxed(false);
+ // We don't know _when_ the interrupt occurred so we have to honor it.
+ const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents();
+ if (wasInterrupted)
+ return false;
+
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;
diff --git a/src/corelib/kernel/qfunctions_winrt_p.h b/src/corelib/kernel/qfunctions_winrt_p.h
index aa32747bc8..d98571906e 100644
--- a/src/corelib/kernel/qfunctions_winrt_p.h
+++ b/src/corelib/kernel/qfunctions_winrt_p.h
@@ -118,7 +118,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
QCoreApplication::processEvents();
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
case ProcessThreadEvents:
@@ -126,7 +126,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
dispatcher->processEvents(QEventLoop::AllEvents);
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
}
@@ -136,7 +136,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
QThread::yieldCurrentThread();
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
}
diff --git a/src/corelib/kernel/qjni.cpp b/src/corelib/kernel/qjni.cpp
index b593483e59..3750fdb9bc 100644
--- a/src/corelib/kernel/qjni.cpp
+++ b/src/corelib/kernel/qjni.cpp
@@ -280,9 +280,7 @@ jclass QJNIEnvironmentPrivate::findClass(const char *className, JNIEnv *env)
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
- const bool found = (clazz != 0) || (clazz == 0 && isCached);
-
- if (found)
+ if (clazz || isCached)
return clazz;
const QLatin1String key(classDotEnc);
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
index ddadc2e114..f7a5fcb686 100644
--- a/src/corelib/kernel/qjnienvironment.cpp
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE
Since \c JNIEnv doesn't do much error checking, such as exception checking and clearing,
QJniEnvironment allows you to do that easily.
+ For more information about JNIEnv, see \l {Java: Interface Function Table}.
+
\note This API has been designed and tested for use with Android.
It has not been tested for other platforms.
*/
@@ -128,29 +130,39 @@ QJniEnvironment::QJniEnvironment()
\fn QJniEnvironment::~QJniEnvironment()
Detaches the current thread from the Java VM and destroys the QJniEnvironment object.
- This will clear any pending exception by calling exceptionCheckAndClear().
+ This will clear any pending exception by calling checkAndClearExceptions().
*/
QJniEnvironment::~QJniEnvironment()
{
- exceptionCheckAndClear();
+ checkAndClearExceptions();
}
/*!
- \fn JNIEnv *QJniEnvironment::operator->()
+ \fn JNIEnv *QJniEnvironment::operator->() const
- Provides access to the QJniEnvironment's JNIEnv pointer.
+ Provides access to the JNI Environment's \c JNIEnv pointer.
*/
-JNIEnv *QJniEnvironment::operator->()
+JNIEnv *QJniEnvironment::operator->() const
{
return d->jniEnv;
}
/*!
- \fn QJniEnvironment::operator JNIEnv *() const
+ \fn JNIEnv &QJniEnvironment::operator*() const
+
+ Returns the JNI Environment's \c JNIEnv object.
+*/
+JNIEnv &QJniEnvironment::operator*() const
+{
+ return *d->jniEnv;
+}
+
+/*!
+ \fn JNIEnv *QJniEnvironment::jniEnv()
- Returns the JNI Environment pointer.
+ Returns the JNI Environment's \c JNIEnv pointer.
*/
-QJniEnvironment::operator JNIEnv* () const
+JNIEnv *QJniEnvironment::jniEnv() const
{
return d->jniEnv;
}
@@ -229,7 +241,7 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
jclass clazz = d->jniEnv->GetObjectClass(classObject.object());
if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) {
- exceptionCheckAndClear();
+ checkAndClearExceptions();
d->jniEnv->DeleteLocalRef(clazz);
return false;
}
@@ -248,7 +260,7 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
*/
/*!
- \fn QJniEnvironment::exceptionCheckAndClear(OutputMode outputMode = OutputMode::Verbose)
+ \fn QJniEnvironment::checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose)
Cleans any pending exceptions either silently or reporting stack backtrace,
depending on the \a outputMode.
@@ -256,11 +268,11 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
In contrast to \l QJniObject, which handles exceptions internally, if you
make JNI calls directly via \c JNIEnv, you need to clear any potential
exceptions after the call using this function. For more information about
- \c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
+ \c JNIEnv calls that can throw an exception, see \l {Java: JNI Functions}{JNI Functions}.
\return \c true when a pending exception was cleared.
*/
-bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputMode)
+bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
{
if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
if (outputMode != OutputMode::Silent)
@@ -274,7 +286,7 @@ bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputM
}
/*!
- \fn QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
+ \fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
Cleans any pending exceptions for \a env, either silently or reporting
stack backtrace, depending on the \a outputMode. This is useful when you
@@ -283,11 +295,11 @@ bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputM
In contrast to \l QJniObject, which handles exceptions internally, if you
make JNI calls directly via \c JNIEnv, you need to clear any potential
exceptions after the call using this function. For more information about
- \c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
+ \c JNIEnv calls that can throw an exception, see \l {Java: JNI Functions}{JNI Functions}.
\return \c true when a pending exception was cleared.
*/
-bool QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
+bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
{
if (Q_UNLIKELY(env->ExceptionCheck())) {
if (outputMode != OutputMode::Silent)
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
index 5a7accf004..a9a3e99097 100644
--- a/src/corelib/kernel/qjnienvironment.h
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -60,8 +60,9 @@ class Q_CORE_EXPORT QJniEnvironment
public:
QJniEnvironment();
~QJniEnvironment();
- JNIEnv *operator->();
- operator JNIEnv *() const;
+ JNIEnv *operator->() const;
+ JNIEnv &operator*() const;
+ JNIEnv *jniEnv() const;
jclass findClass(const char *className);
static JavaVM *javaVM();
bool registerNativeMethods(const char *className, JNINativeMethod methods[], int size);
@@ -71,9 +72,8 @@ public:
Verbose
};
- bool exceptionCheckAndClear(OutputMode outputMode = OutputMode::Verbose);
- static bool exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
-
+ bool checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose);
+ static bool checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
private:
Q_DISABLE_COPY_MOVE(QJniEnvironment)
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index 45f4a4d895..0ad5988297 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -291,11 +291,11 @@ void QtAndroidPrivate::handleResume()
static void setAndroidSdkVersion(JNIEnv *env)
{
jclass androidVersionClass = env->FindClass("android/os/Build$VERSION");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return;
jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return;
g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID);
@@ -331,42 +331,42 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
{
jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
"activity",
"()Landroid/app/Activity;");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
"service",
"()Landroid/app/Service;");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
"classLoader",
"()Ljava/lang/ClassLoader;");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
setAndroidSdkVersion(env);
@@ -394,7 +394,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
- if (!regOk && QJniEnvironment::exceptionCheckAndClear(env))
+ if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
@@ -512,7 +512,7 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env,
QJniEnvironment env;
jclass clazz = env->FindClass("java/lang/String");
- if (env.exceptionCheckAndClear())
+ if (env.checkAndClearExceptions())
return;
auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
index 43840052ae..418a404bb4 100644
--- a/src/corelib/kernel/qjniobject.cpp
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -132,7 +132,7 @@ QT_BEGIN_NAMESPACE
\note The user must handle exceptions manually when doing JNI calls using \c JNIEnv directly.
It is unsafe to make other JNI calls when exceptions are pending. For more information, see
- QJniEnvironment::exceptionCheckAndClear().
+ QJniEnvironment::checkAndClearExceptions().
\section1 Java Native Methods
@@ -160,7 +160,7 @@ QT_BEGIN_NAMESPACE
\l {JNI Design Overview: Global and Local References}. Local references
created outside a native method scope must be deleted manually, since
the garbage collector will not free them automatically because we are using
- \c AttachCurrentThread. For more information, see
+ \l {Java: AttachCurrentThread}{AttachCurrentThread}. For more information, see
\l {JNI tips: Local and global references}.
If you want to keep a Java object alive you need to either create a new global
@@ -346,7 +346,7 @@ inline static jclass loadClass(const QByteArray &className, JNIEnv *env, bool bi
"(Ljava/lang/String;)Ljava/lang/Class;",
stringName.object());
- if (!QJniEnvironment::exceptionCheckAndClear(env) && classObject.isValid())
+ if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid())
clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
cachedClasses->insert(key, clazz);
@@ -366,7 +366,7 @@ static inline jmethodID getMethodID(JNIEnv *env,
jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature)
: env->GetMethodID(clazz, name, signature);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return nullptr;
return id;
@@ -420,7 +420,7 @@ static inline jfieldID getFieldID(JNIEnv *env,
jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature)
: env->GetFieldID(clazz, name, signature);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return nullptr;
return id;
@@ -467,9 +467,7 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
- const bool found = clazz || (!clazz && isCached);
-
- if (found)
+ if (clazz || isCached)
return clazz;
const QLatin1String key(classDotEnc);
@@ -481,7 +479,7 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
return it.value();
jclass fclazz = env->FindClass(className);
- if (!QJniEnvironment::exceptionCheckAndClear(env)) {
+ if (!QJniEnvironment::checkAndClearExceptions(env)) {
clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
env->DeleteLocalRef(fclazz);
}
@@ -491,7 +489,7 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
}
if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader...
- clazz = loadClass(classDotEnc, QJniEnvironment(), true);
+ clazz = loadClass(classDotEnc, QJniEnvironment().jniEnv(), true);
return clazz;
}
@@ -540,11 +538,11 @@ QJniObject::QJniObject(const char *className)
{
QJniEnvironment env;
d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
d->m_own_jclass = false;
if (d->m_jclass) {
// get default constructor
- jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", "()V");
+ jmethodID constructorId = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, "<init>", "()V");
if (constructorId) {
jobject obj = env->NewObject(d->m_jclass, constructorId);
if (obj) {
@@ -573,10 +571,10 @@ QJniObject::QJniObject(const char *className, const char *signature, ...)
{
QJniEnvironment env;
d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
d->m_own_jclass = false;
if (d->m_jclass) {
- jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", signature);
+ jmethodID constructorId = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, "<init>", signature);
if (constructorId) {
va_list args;
va_start(args, signature);
@@ -595,10 +593,10 @@ QJniObject::QJniObject(const char *className, const char *signature, const QVaLi
{
QJniEnvironment env;
d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
d->m_own_jclass = false;
if (d->m_jclass) {
- jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", signature);
+ jmethodID constructorId = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, "<init>", signature);
if (constructorId) {
jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
if (obj) {
@@ -628,7 +626,7 @@ QJniObject::QJniObject(jclass clazz, const char *signature, ...)
if (clazz) {
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (d->m_jclass) {
- jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", signature);
+ jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
if (constructorId) {
va_list args;
va_start(args, signature);
@@ -660,7 +658,7 @@ QJniObject::QJniObject(jclass clazz)
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (d->m_jclass) {
// get default constructor
- jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", "()V");
+ jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", "()V");
if (constructorId) {
jobject obj = env->NewObject(d->m_jclass, constructorId);
if (obj) {
@@ -678,7 +676,7 @@ QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate
if (clazz) {
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (d->m_jclass) {
- jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", signature);
+ jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
if (constructorId) {
jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
if (obj) {
@@ -728,7 +726,7 @@ inline static QJniObject getCleanJniObject(jobject obj)
return QJniObject();
QJniEnvironment env;
- if (env.exceptionCheckAndClear()) {
+ if (env.checkAndClearExceptions()) {
env->DeleteLocalRef(obj);
return QJniObject();
}
@@ -774,10 +772,10 @@ QJniObject QJniObject::callObjectMethodV(const char *methodName,
{
QJniEnvironment env;
jobject res = nullptr;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ jmethodID id = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, methodName, signature);
if (id) {
res = env->CallObjectMethodV(d->m_jobject, id, args);
- if (env.exceptionCheckAndClear()) {
+ if (env.checkAndClearExceptions()) {
env->DeleteLocalRef(res);
res = nullptr;
}
@@ -795,13 +793,13 @@ QJniObject QJniObject::callStaticObjectMethodV(const char *className,
{
QJniEnvironment env;
jobject res = nullptr;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
res = env->CallStaticObjectMethodV(clazz, id, args);
- if (env.exceptionCheckAndClear()) {
+ if (env.checkAndClearExceptions()) {
env->DeleteLocalRef(res);
res = nullptr;
}
@@ -819,7 +817,7 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
va_list args)
{
QJniEnvironment env;
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (!id)
return QJniObject();
@@ -842,13 +840,13 @@ template <>
Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName, const char *signature, ...) const
{
QJniEnvironment env;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ jmethodID id = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, methodName, signature);
if (id) {
va_list args;
va_start(args, signature);
env->CallVoidMethodV(d->m_jobject, id, args);
va_end(args);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -887,16 +885,16 @@ Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className,
...)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
env->CallStaticVoidMethodV(clazz, id, args);
va_end(args);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
}
@@ -938,13 +936,13 @@ Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz,
{
QJniEnvironment env;
if (clazz) {
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
env->CallStaticVoidMethodV(clazz, id, args);
va_end(args);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
}
@@ -956,14 +954,14 @@ Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(const char *className,
va_list args)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz,
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
toBinaryEncClassName(className), methodName,
signature, true);
if (id) {
env->CallStaticVoidMethodV(clazz, id, args);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
}
@@ -975,10 +973,10 @@ Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(jclass clazz,
va_list args)
{
QJniEnvironment env;
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
env->CallStaticVoidMethodV(clazz, id, args);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -1004,10 +1002,10 @@ Q_CORE_EXPORT void QJniObject::callMethodV<void>(const char *methodName, const c
va_list args) const
{
QJniEnvironment env;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ jmethodID id = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, methodName, signature);
if (id) {
env->CallVoidMethodV(d->m_jobject, id, args);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -1017,13 +1015,13 @@ template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodNa
{ \
QJniEnvironment env; \
Type res = 0; \
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature); \
+ jmethodID id = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, methodName, signature); \
if (id) { \
va_list args; \
va_start(args, signature); \
res = env->Call##MethodName##MethodV(d->m_jobject, id, args); \
va_end(args); \
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
} \
return res; \
@@ -1040,16 +1038,16 @@ template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *cl
{ \
QJniEnvironment env; \
Type res = 0; \
- jclass clazz = loadClass(className, env); \
+ jclass clazz = loadClass(className, env.jniEnv()); \
if (clazz) { \
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, \
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className), methodName, \
signature, true); \
if (id) { \
va_list args; \
va_start(args, signature); \
res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
va_end(args); \
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
} \
} \
@@ -1069,13 +1067,13 @@ template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz,
QJniEnvironment env; \
Type res = 0; \
if (clazz) { \
- jmethodID id = getMethodID(env, clazz, methodName, signature, true); \
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true); \
if (id) { \
va_list args; \
va_start(args, signature); \
res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
va_end(args); \
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
} \
} \
@@ -1092,10 +1090,10 @@ Q_CORE_EXPORT Type QJniObject::callMethodV<Type>(const char *methodName, const c
{\
QJniEnvironment env;\
Type res = 0;\
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);\
+ jmethodID id = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, methodName, signature);\
if (id) {\
res = env->Call##MethodName##MethodV(d->m_jobject, id, args);\
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
}\
return res;\
@@ -1108,13 +1106,13 @@ Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(const char *className,\
{\
QJniEnvironment env;\
Type res = 0;\
- jclass clazz = loadClass(className, env);\
+ jclass clazz = loadClass(className, env.jniEnv());\
if (clazz) {\
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName,\
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className), methodName,\
signature, true);\
if (id) {\
res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
}\
}\
@@ -1128,10 +1126,10 @@ Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(jclass clazz,\
{\
QJniEnvironment env;\
Type res = 0;\
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);\
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);\
if (id) {\
res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
}\
return res;\
@@ -1164,7 +1162,7 @@ DECLARE_JNI_METHODS(Double, jdouble, "()D")
QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
{
QJniEnvironment env;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ jmethodID id = getCachedMethodID(env.jniEnv(), d->m_jclass, d->m_className, methodName, signature);
if (id) {
va_list args;
va_start(args, signature);
@@ -1195,9 +1193,9 @@ QJniObject QJniObject::callStaticObjectMethod(const char *className,
...)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
va_list args;
@@ -1224,7 +1222,7 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz,
{
QJniEnvironment env;
if (clazz) {
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
@@ -1351,15 +1349,15 @@ Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(const char *className,
jobject value)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (!clazz)
return;
- jfieldID id = getCachedFieldID(env, clazz, className, fieldName, signature, true);
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, signature, true);
if (id) {
env->SetStaticObjectField(clazz, id, value);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -1375,11 +1373,11 @@ template <> Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(jclass clazz,
jobject value)
{
QJniEnvironment env;
- jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
if (id) {
env->SetStaticObjectField(clazz, id, value);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -1422,10 +1420,10 @@ template <> Q_CORE_EXPORT Type QJniObject::getField<Type>(const char *fieldName)
{ \
QJniEnvironment env; \
Type res = 0; \
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
+ jfieldID id = getCachedFieldID(env.jniEnv(), d->m_jclass, d->m_className, fieldName, Signature); \
if (id) {\
res = env->Get##FieldName##Field(d->m_jobject, id); \
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
} \
return res;\
@@ -1434,15 +1432,15 @@ template <> \
Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(const char *className, const char *fieldName) \
{ \
QJniEnvironment env; \
- jclass clazz = loadClass(className, env); \
+ jclass clazz = loadClass(className, env.jniEnv()); \
if (!clazz) \
return 0; \
- jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName, \
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz, toBinaryEncClassName(className), fieldName, \
Signature, true); \
if (!id) \
return 0; \
Type res = env->GetStatic##FieldName##Field(clazz, id); \
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
return res;\
} \
@@ -1451,10 +1449,10 @@ Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(jclass clazz, const char *fi
{\
QJniEnvironment env;\
Type res = 0;\
- jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, Signature, true);\
if (id) {\
res = env->GetStatic##FieldName##Field(clazz, id);\
- if (env.exceptionCheckAndClear()) \
+ if (env.checkAndClearExceptions()) \
res = 0; \
}\
return res;\
@@ -1464,33 +1462,33 @@ template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *clas
Type value) \
{ \
QJniEnvironment env; \
- jclass clazz = loadClass(className, env); \
+ jclass clazz = loadClass(className, env.jniEnv()); \
if (!clazz) \
return; \
- jfieldID id = getCachedFieldID(env, clazz, className, fieldName, Signature, true); \
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName, Signature, true); \
if (!id) \
return; \
env->SetStatic##FieldName##Field(clazz, id, value); \
- env.exceptionCheckAndClear(); \
+ env.checkAndClearExceptions(); \
}\
template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(jclass clazz,\
const char *fieldName,\
Type value)\
{\
QJniEnvironment env;\
- jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, Signature, true);\
if (id) {\
env->SetStatic##FieldName##Field(clazz, id, value);\
- env.exceptionCheckAndClear();\
+ env.checkAndClearExceptions();\
}\
}\
template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
{ \
QJniEnvironment env; \
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
+ jfieldID id = getCachedFieldID(env.jniEnv(), d->m_jclass, d->m_className, fieldName, Signature); \
if (id) { \
env->Set##FieldName##Field(d->m_jobject, id, value); \
- env.exceptionCheckAndClear(); \
+ env.checkAndClearExceptions(); \
} \
} \
@@ -1523,10 +1521,10 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
const char *signature)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (!clazz)
return QJniObject();
- jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName,
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz, toBinaryEncClassName(className), fieldName,
signature, true);
if (!id)
return QJniObject();
@@ -1551,7 +1549,7 @@ QJniObject QJniObject::getStaticObjectField(jclass clazz,
const char *signature)
{
QJniEnvironment env;
- jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
if (!id)
return QJniObject();
@@ -1630,10 +1628,10 @@ template <> Q_CORE_EXPORT
void QJniObject::setField<jobject>(const char *fieldName, const char *signature, jobject value)
{
QJniEnvironment env;
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ jfieldID id = getCachedFieldID(env.jniEnv(), d->m_jclass, d->m_className, fieldName, signature);
if (id) {
env->SetObjectField(d->m_jobject, id, value);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -1643,10 +1641,10 @@ void QJniObject::setField<jobjectArray>(const char *fieldName,
jobjectArray value)
{
QJniEnvironment env;
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ jfieldID id = getCachedFieldID(env.jniEnv(), d->m_jclass, d->m_className, fieldName, signature);
if (id) {
env->SetObjectField(d->m_jobject, id, value);
- env.exceptionCheckAndClear();
+ env.checkAndClearExceptions();
}
}
@@ -1674,7 +1672,7 @@ void QJniObject::setField<jobjectArray>(const char *fieldName,
QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
{
QJniEnvironment env;
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ jfieldID id = getCachedFieldID(env.jniEnv(), d->m_jclass, d->m_className, fieldName, signature);
if (!id)
return QJniObject();
@@ -1817,10 +1815,10 @@ bool QJniObject::isClassAvailable(const char *className)
{
QJniEnvironment env;
- if (!env)
+ if (!env.jniEnv())
return false;
- return loadClass(className, env);;
+ return loadClass(className, env.jniEnv());;
}
/*!
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 650ea60617..a574bec179 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -384,6 +384,33 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const
{
return QCoreApplication::translate(objectClassName(this), s, c, n);
}
+
+/*!
+ \since 6.2
+ Returns the metatype corresponding to this metaobject.
+ If the metaobject originates from a namespace, an invalid metatype is returned.
+ */
+QMetaType QMetaObject::metaType() const
+{
+
+ const QMetaObjectPrivate *d = priv(this->d.data);
+ if (d->revision < 10) {
+ // before revision 10, we did not store the metatype in the metatype array
+ return QMetaType::fromName(className());
+ } else {
+ /* in the metatype array, we store
+ idx: 0 propertyCount - 1 propertyCount
+ data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),...
+ */
+ auto iface = this->d.metaTypes[d->propertyCount];
+ if (iface == QtPrivate::qMetaTypeInterfaceForType<void>())
+ return QMetaType(); // return invalid meta-type for namespaces
+ if (iface)
+ return QMetaType(iface);
+ else // in case of a dynamic metaobject, we might have no metatype stored
+ return QMetaType::fromName(className()); // try lookup by name in that case
+ }
+}
#endif // QT_NO_TRANSLATION
/*!
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index dbd82ba27b..e35d0b5e0c 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -172,7 +172,8 @@ struct QMetaObjectPrivate
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
// revision 9 is Qt 6.0: It adds the metatype of properties and methods
- enum { OutputRevision = 9 }; // Used by moc, qmetaobjectbuilder and qdbus
+ // revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array
+ enum { OutputRevision = 10 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index c842cd68b9..8c55bdb3ae 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -1203,7 +1203,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
- int(d->methods.size()) // return "parameters" don't have names
- int(d->constructors.size()); // "this" parameters don't have names
if constexpr (mode == Construct) {
- static_assert(QMetaObjectPrivate::OutputRevision == 9, "QMetaObjectBuilder should generate the same version as moc");
+ static_assert(QMetaObjectPrivate::OutputRevision == 10, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags;
pmeta->className = 0; // Class name is always the first string.
@@ -1281,7 +1281,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
// Output the methods in the class.
Q_ASSERT(!buf || dataIndex == pmeta->methodData);
- int parameterMetaTypesIndex = int(d->properties.size());
+ // + 1 for metatype of this metaobject
+ int parameterMetaTypesIndex = int(d->properties.size()) + 1;
for (const auto &method : d->methods) {
[[maybe_unused]] int name = strings.enter(method.name());
int argc = method.parameterCount();
@@ -1448,6 +1449,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
types++;
}
+ // add metatype interface for this metaobject - must be null
+ // as we can't know our metatype
+ *types = nullptr;
+ types++;
for (const auto &method: d->methods) {
QMetaType mt(QMetaType::fromName(method.returnType).id());
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
@@ -1526,274 +1531,6 @@ void QMetaObjectBuilder::setStaticMetacallFunction
d->staticMetacallFunction = value;
}
-#ifndef QT_NO_DATASTREAM
-
-/*!
- Serializes the contents of the meta object builder onto \a stream.
-
- \sa deserialize()
-*/
-void QMetaObjectBuilder::serialize(QDataStream &stream) const
-{
- int index;
-
- // Write the class and super class names.
- stream << d->className;
- if (d->superClass)
- stream << QByteArray(d->superClass->className());
- else
- stream << QByteArray();
-
- // Write the counts for each type of class member.
- stream << int(d->classInfoNames.size());
- stream << int(d->methods.size());
- stream << int(d->properties.size());
- stream << int(d->enumerators.size());
- stream << int(d->constructors.size());
- stream << int(d->relatedMetaObjects.size());
-
- // Write the items of class information.
- for (index = 0; index < d->classInfoNames.size(); ++index) {
- stream << d->classInfoNames[index];
- stream << d->classInfoValues[index];
- }
-
- // Write the methods.
- for (const auto &method : d->methods) {
- stream << method.signature;
- stream << method.returnType;
- stream << method.parameterNames;
- stream << method.tag;
- stream << method.attributes;
- if (method.revision)
- stream << method.revision;
- }
-
- // Write the properties.
- for (const auto &property : d->properties) {
- stream << property.name;
- stream << property.type;
- stream << property.flags;
- stream << property.notifySignal;
- stream << property.revision;
- }
-
- // Write the enumerators.
- for (const auto &enumerator : d->enumerators) {
- stream << enumerator.name;
- stream << enumerator.isFlag;
- stream << enumerator.isScoped;
- stream << enumerator.keys;
- stream << enumerator.values;
- }
-
- // Write the constructors.
- for (const auto &ctor : d->constructors) {
- stream << ctor.signature;
- stream << ctor.returnType;
- stream << ctor.parameterNames;
- stream << ctor.tag;
- stream << ctor.attributes;
- }
-
- // Write the related meta objects.
- for (index = 0; index < d->relatedMetaObjects.size(); ++index) {
- const QMetaObject *meta = d->relatedMetaObjects[index];
- stream << QByteArray(meta->className());
- }
-
- // Add an extra empty QByteArray for additional data in future versions.
- // This should help maintain backwards compatibility, allowing older
- // versions to read newer data.
- stream << QByteArray();
-}
-
-// Resolve a class name using the name reference map.
-static const QMetaObject *resolveClassName(const QMap<QByteArray, const QMetaObject *> &references,
- const QByteArray &name)
-{
- if (name == QByteArray("QObject"))
- return &QObject::staticMetaObject;
- else
- return references.value(name, nullptr);
-}
-
-/*!
- Deserializes a meta object builder from \a stream into
- this meta object builder.
-
- The \a references parameter specifies a mapping from class names
- to QMetaObject instances for resolving the super class name and
- related meta objects in the object that is deserialized.
- The meta object for QObject is implicitly added to \a references
- and does not need to be supplied.
-
- The QDataStream::status() value on \a stream will be set to
- QDataStream::ReadCorruptData if the input data is corrupt.
- The status will be set to QDataStream::ReadPastEnd if the
- input was exhausted before the full meta object was read.
-
- \sa serialize()
-*/
-void QMetaObjectBuilder::deserialize
- (QDataStream& stream,
- const QMap<QByteArray, const QMetaObject *>& references)
-{
- QByteArray name;
- const QMetaObject *cl;
- int index;
-
- // Clear all members in the builder to their default states.
- d->className.clear();
- d->superClass = &QObject::staticMetaObject;
- d->classInfoNames.clear();
- d->classInfoValues.clear();
- d->methods.clear();
- d->properties.clear();
- d->enumerators.clear();
- d->constructors.clear();
- d->relatedMetaObjects.clear();
- d->staticMetacallFunction = nullptr;
-
- // Read the class and super class names.
- stream >> d->className;
- stream >> name;
- if (name.isEmpty()) {
- d->superClass = nullptr;
- } else if ((cl = resolveClassName(references, name)) != nullptr) {
- d->superClass = cl;
- } else {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
-
- // Read the counts for each type of class member.
- int classInfoCount, methodCount, propertyCount;
- int enumeratorCount, constructorCount, relatedMetaObjectCount;
- stream >> classInfoCount;
- stream >> methodCount;
- stream >> propertyCount;
- stream >> enumeratorCount;
- stream >> constructorCount;
- stream >> relatedMetaObjectCount;
- if (classInfoCount < 0 || methodCount < 0 ||
- propertyCount < 0 || enumeratorCount < 0 ||
- constructorCount < 0 || relatedMetaObjectCount < 0) {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
-
- // Read the items of class information.
- for (index = 0; index < classInfoCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- QByteArray value;
- stream >> name;
- stream >> value;
- addClassInfo(name, value);
- }
-
- // Read the member methods.
- for (index = 0; index < methodCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addMethod(name);
- QMetaMethodBuilderPrivate &method = d->methods[index];
- stream >> method.returnType;
- stream >> method.parameterNames;
- stream >> method.tag;
- stream >> method.attributes;
- if (method.attributes & MethodRevisioned)
- stream >> method.revision;
- if (method.methodType() == QMetaMethod::Constructor) {
- // Cannot add a constructor in this set of methods.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the properties.
- for (index = 0; index < propertyCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- QByteArray type;
- stream >> name;
- stream >> type;
- addProperty(name, type);
- QMetaPropertyBuilderPrivate &property = d->properties[index];
- stream >> property.flags;
- stream >> property.notifySignal;
- if (property.notifySignal < -1 ||
- property.notifySignal >= int(d->methods.size())) {
- // Notify signal method index is out of range.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- if (property.notifySignal >= 0 &&
- d->methods[property.notifySignal].methodType() != QMetaMethod::Signal) {
- // Notify signal method index does not refer to a signal.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- stream >> property.revision;
- }
-
- // Read the enumerators.
- for (index = 0; index < enumeratorCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addEnumerator(name);
- QMetaEnumBuilderPrivate &enumerator = d->enumerators[index];
- stream >> enumerator.isFlag;
- stream >> enumerator.isScoped;
- stream >> enumerator.keys;
- stream >> enumerator.values;
- if (enumerator.keys.size() != enumerator.values.size()) {
- // Mismatch between number of keys and number of values.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the constructor methods.
- for (index = 0; index < constructorCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addConstructor(name);
- QMetaMethodBuilderPrivate &method = d->constructors[index];
- stream >> method.returnType;
- stream >> method.parameterNames;
- stream >> method.tag;
- stream >> method.attributes;
- if (method.methodType() != QMetaMethod::Constructor) {
- // The type must be Constructor.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the related meta objects.
- for (index = 0; index < relatedMetaObjectCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- cl = resolveClassName(references, name);
- if (!cl) {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- addRelatedMetaObject(cl);
- }
-
- // Read the extra data block, which is reserved for future use.
- stream >> name;
-}
-
-#endif // !QT_NO_DATASTREAM
-
/*!
\class QMetaMethodBuilder
\inmodule QtCore
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 0f545008f2..0435e9cd97 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -168,13 +168,6 @@ public:
QMetaObject *toMetaObject() const;
-#ifndef QT_NO_DATASTREAM
- void serialize(QDataStream& stream) const;
- void deserialize
- (QDataStream& stream,
- const QMap<QByteArray, const QMetaObject *>& references);
-#endif
-
private:
Q_DISABLE_COPY_MOVE(QMetaObjectBuilder)
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index ac792a2f27..43e633faaf 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -37,7 +37,9 @@
**
****************************************************************************/
+#define QT_QMETATYPE_BC_COMPAT 1
#include "qmetatype.h"
+#undef QT_QMETATYPE_BC_COMPAT
#include "qmetatype_p.h"
#include "qobjectdefs.h"
#include "qdatetime.h"
@@ -489,6 +491,18 @@ bool QMetaType::isRegistered() const
Returns id type hold by this QMetatype instance.
*/
+// keep in sync with version in header
+// ### Qt 7::remove BC helper
+int QMetaType::id() const
+{
+ if (d_ptr) {
+ if (int id = d_ptr->typeId.loadRelaxed())
+ return id;
+ return idHelper();
+ }
+ return 0;
+}
+
/*!
\internal
The slowpath of id(). Precondition: d_ptr != nullptr
@@ -504,7 +518,7 @@ int QMetaType::idHelper() const
}
/*!
- \fn constexpr bool QMetaType::sizeOf() const
+ \fn constexpr qsizetype QMetaType::sizeOf() const
\since 5.0
Returns the size of the type in bytes (i.e. sizeof(T),
@@ -574,7 +588,7 @@ int QMetaType::idHelper() const
*/
void *QMetaType::create(const void *copy) const
{
- if (d_ptr) {
+ if (d_ptr && (copy ? !!d_ptr->copyCtr : !!d_ptr->defaultCtr)) {
void *where =
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ?
@@ -1835,7 +1849,7 @@ static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toTy
static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType, void *to)
{
int fromTypeId = fromType.id();
- qlonglong value;
+ qlonglong value = -1;
bool ok = false;
#ifndef QT_NO_QOBJECT
if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray) {
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index bdea0b2879..1d039b6f18 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -268,7 +268,7 @@ public:
mutable QBasicAtomicInt typeId;
using MetaObjectFn = const QMetaObject *(*)(const QMetaTypeInterface *);
- const MetaObjectFn metaObjectFn;
+ MetaObjectFn metaObjectFn;
const char *name;
@@ -444,7 +444,12 @@ public:
bool isValid() const;
bool isRegistered() const;
- int id() const
+#if defined(QT_QMETATYPE_BC_COMPAT) || defined(Q_QDOC)
+ int id() const;
+#else
+ // ### Qt 7: Remove traces of out of line version
+ // unused int parameter is used to avoid ODR violation
+ int id(int = 0) const
{
if (d_ptr) {
if (int id = d_ptr->typeId.loadRelaxed())
@@ -453,6 +458,7 @@ public:
}
return 0;
};
+#endif
constexpr qsizetype sizeOf() const;
constexpr qsizetype alignOf() const;
constexpr TypeFlags flags() const;
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index a729962d4f..343922581c 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -167,7 +167,6 @@ extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *)
#endif
void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = nullptr;
-void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = nullptr;
void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = nullptr;
int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
bool (*QAbstractDeclarativeData::isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
@@ -2110,8 +2109,6 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
- if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
- QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}
/*!
@@ -5246,5 +5243,4 @@ bool QMetaObject::Connection::isConnected_helper() const
QT_END_NAMESPACE
-#include "moc_qnamespace.cpp"
#include "moc_qobject.cpp"
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index 4483e2b6ce..019473018f 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -89,7 +89,6 @@ class Q_CORE_EXPORT QAbstractDeclarativeData
{
public:
static void (*destroyed)(QAbstractDeclarativeData *, QObject *);
- static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *);
static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **);
static int (*receivers)(QAbstractDeclarativeData *, const QObject *, int);
static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int);
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 5ade222ac5..00a6b62eab 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -173,6 +173,8 @@ struct Q_CORE_EXPORT QMetaObject
QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION
+ QMetaType metaType() const;
+
int methodOffset() const;
int enumeratorOffset() const;
int propertyOffset() const;
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 009ce09e56..7c8477e31a 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -66,16 +66,17 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
{
- if (auto *binding = bindingPtr()) {
- observer->prev = &binding->firstObserver.ptr;
- observer->next = binding->firstObserver.ptr;
+ if (auto *b = binding()) {
+ observer->prev = &b->firstObserver.ptr;
+ observer->next = b->firstObserver.ptr;
if (observer->next)
observer->next->prev = &observer->next;
- binding->firstObserver.ptr = observer;
+ b->firstObserver.ptr = observer;
} else {
- Q_ASSERT(!(ptr->d_ptr & QPropertyBindingData::BindingBit));
- auto firstObserver = reinterpret_cast<QPropertyObserver*>(ptr->d_ptr);
- observer->prev = reinterpret_cast<QPropertyObserver**>(&ptr->d_ptr);
+ auto &d = ptr->d_ref();
+ Q_ASSERT(!(d & QPropertyBindingData::BindingBit));
+ auto firstObserver = reinterpret_cast<QPropertyObserver*>(d);
+ observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
observer->next = firstObserver;
if (observer->next)
observer->next->prev = &observer->next;
@@ -83,6 +84,182 @@ void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
setFirstObserver(observer);
}
+/*!
+ \internal
+
+ QPropertyDelayedNotifications is used to manage delayed notifications in grouped property updates.
+ It acts as a pool allocator for QPropertyProxyBindingData, and has methods to manage delayed
+ notifications.
+
+ \sa beginPropertyUpdateGroup, endPropertyUpdateGroup
+ */
+struct QPropertyDelayedNotifications
+{
+ // we can't access the dynamic page size as we need a constant value
+ // use 4096 as a sensible default
+ static constexpr inline auto PageSize = 4096;
+ int ref = 0;
+ QPropertyDelayedNotifications *next = nullptr; // in case we have more than size dirty properties...
+ qsizetype used = 0;
+ // Size chosen to avoid allocating more than one page of memory, while still ensuring
+ // that we can store many delayed properties without doing further allocations
+ static constexpr qsizetype size = (PageSize - 3*sizeof(void *))/sizeof(QPropertyProxyBindingData);
+ QPropertyProxyBindingData delayedProperties[size];
+
+ /*!
+ \internal
+ This method is called when a property attempts to notify its observers while inside of a
+ property update group. Instead of actually notifying, it replaces \a bindingData's d_ptr
+ with a QPropertyProxyBindingData.
+ \a bindingData and \a propertyData are the binding data and property data of the property
+ whose notify call gets delayed.
+ \sa QPropertyBindingData::notifyObservers
+ */
+ void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData) {
+ if (bindingData->isNotificationDelayed())
+ return;
+ auto *data = this;
+ while (data->used == size) {
+ if (!data->next)
+ // add a new page
+ data->next = new QPropertyDelayedNotifications;
+ data = data->next;
+ }
+ auto *delayed = data->delayedProperties + data->used;
+ *delayed = QPropertyProxyBindingData { bindingData->d_ptr, bindingData, propertyData };
+ ++data->used;
+ // preserve the binding bit for faster access
+ quintptr bindingBit = bindingData->d_ptr & QPropertyBindingData::BindingBit;
+ bindingData->d_ptr = reinterpret_cast<quintptr>(delayed) | QPropertyBindingData::DelayedNotificationBit | bindingBit;
+ Q_ASSERT(bindingData->d_ptr > 3);
+ if (!bindingBit) {
+ if (auto observer = reinterpret_cast<QPropertyObserver *>(delayed->d_ptr))
+ observer->prev = reinterpret_cast<QPropertyObserver **>(&delayed->d_ptr);
+ }
+ }
+
+ /*!
+ \internal
+ Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
+ \a index, it
+ \list
+ \li restores the original binding data that was modified in addProperty and
+ \li evaluates any bindings which depend on properties that were changed inside
+ the group.
+ \endlist
+ Change notifications are sent later with notify (following the logic of separating
+ binding updates and notifications used in non-deferred updates).
+ */
+ void evaluateBindings(int index) {
+ auto *delayed = delayedProperties + index;
+ auto *bindingData = delayed->originalBindingData;
+ if (!bindingData)
+ return;
+
+ bindingData->d_ptr = delayed->d_ptr;
+ Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
+ if (!bindingData->hasBinding()) {
+ if (auto observer = reinterpret_cast<QPropertyObserver *>(bindingData->d_ptr))
+ observer->prev = reinterpret_cast<QPropertyObserver **>(&bindingData->d_ptr);
+ }
+
+ QPropertyBindingDataPointer bindingDataPointer{bindingData};
+ QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
+ if (observer)
+ observer.evaluateBindings();
+ }
+
+ /*!
+ \internal
+ Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
+ \a i, it
+ \list
+ \li resets the proxy binding data and
+ \li sends any pending notifications.
+ \endlist
+ */
+ void notify(int index) {
+ auto *delayed = delayedProperties + index;
+ auto *bindingData = delayed->originalBindingData;
+ if (!bindingData)
+ return;
+
+ delayed->originalBindingData = nullptr;
+ delayed->d_ptr = 0;
+
+ QPropertyBindingDataPointer bindingDataPointer{bindingData};
+ QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
+ if (observer)
+ observer.notify(delayed->propertyData);
+ }
+};
+
+static thread_local QPropertyDelayedNotifications *groupUpdateData = nullptr;
+
+/*!
+ \since 6.2
+
+ \relates template<typename T> QProperty<T>
+
+ Marks the beginning of a property update group. Inside this group,
+ changing a property does neither immediately update any dependent properties
+ nor does it trigger change notifications.
+ Those are instead deferred until the group is ended by a call to endPropertyUpdateGroup.
+
+ Groups can be nested. In that case, the deferral ends only after the outermost group has been
+ ended.
+
+ \note Change notifications are only send after all property values affected by the group have
+ been updated to their new values. This allows re-establishing a class invariant if multiple
+ properties need to be updated, preventing any external observer from noticing an inconsistent
+ state.
+
+ \sa Qt::endPropertyUpdateGroup
+ */
+void Qt::beginPropertyUpdateGroup()
+{
+ if (!groupUpdateData)
+ groupUpdateData = new QPropertyDelayedNotifications;
+ ++groupUpdateData->ref;
+}
+
+/*!
+ \since 6.2
+ \relates template<typename T> QProperty<T>
+
+ Ends a property update group. If the outermost group has been ended, and deferred
+ binding evaluations and notifications happen now.
+
+ \warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
+ results in undefined behavior.
+
+ \sa Qt::beginPropertyUpdateGroup
+ */
+void Qt::endPropertyUpdateGroup()
+{
+ auto *data = groupUpdateData;
+ Q_ASSERT(data->ref);
+ if (--data->ref)
+ return;
+ groupUpdateData = nullptr;
+ // update all delayed properties
+ auto start = data;
+ while (data) {
+ for (int i = 0; i < data->used; ++i)
+ data->evaluateBindings(i);
+ data = data->next;
+ }
+ // notify all delayed properties
+ data = start;
+ while (data) {
+ for (int i = 0; i < data->used; ++i)
+ data->notify(i);
+ auto *next = data->next;
+ delete data;
+ data = next;
+ }
+}
+
QPropertyBindingPrivate::~QPropertyBindingPrivate()
{
if (firstObserver)
@@ -98,43 +275,16 @@ void QPropertyBindingPrivate::unlinkAndDeref()
destroyAndFreeMemory(this);
}
-void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
+void QPropertyBindingPrivate::evaluateRecursive()
{
- if (eagerlyUpdating) {
+ if (updating) {
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
if (isQQmlPropertyBinding)
errorCallBack(this);
return;
}
- if (dirty)
- return;
- dirty = true;
-
- eagerlyUpdating = true;
- QScopeGuard guard([&](){eagerlyUpdating = false;});
- bool knownToHaveChanged = false;
- if (requiresEagerEvaluation()) {
- // these are compat properties that we will need to evaluate eagerly
- if (!evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
- return;
- knownToHaveChanged = true;
- }
- if (firstObserver)
- firstObserver.notify(this, propertyDataPtr, knownToHaveChanged);
- if (hasStaticObserver)
- staticObserverCallback(propertyDataPtr);
-}
-
-bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status)
-{
- Q_ASSERT(dirty);
- if (updating) {
- error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
- if (isQQmlPropertyBinding)
- errorCallBack(this);
- return false;
- }
+ QScopedValueRollback<bool> updateGuard(updating, true);
/*
* Evaluating the binding might lead to the binding being broken. This can
@@ -145,23 +295,42 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(
* that the object is still alive when updateGuard's dtor runs.
*/
QPropertyBindingPrivatePtr keepAlive {this};
- QScopedValueRollback<bool> updateGuard(updating, true);
- BindingEvaluationState evaluationFrame(this, status);
+ BindingEvaluationState evaluationFrame(this);
+ auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
+ QPropertyBindingPrivate::getSizeEnsuringAlignment();
bool changed = false;
-
- Q_ASSERT(propertyDataPtr == data);
- QUntypedPropertyData *mutable_data = const_cast<QUntypedPropertyData *>(data);
-
if (hasBindingWrapper) {
- changed = staticBindingWrapper(metaType, mutable_data, {vtable, reinterpret_cast<std::byte *>(this)+QPropertyBindingPrivate::getSizeEnsuringAlignment()});
+ changed = staticBindingWrapper(metaType, propertyDataPtr,
+ {vtable, bindingFunctor});
} else {
- changed = vtable->call(metaType, mutable_data, reinterpret_cast<std::byte *>(this)+ QPropertyBindingPrivate::getSizeEnsuringAlignment());
+ changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
}
+ // If there was a change, we must set pendingNotify.
+ // If there was not, we must not clear it, as that only should happen in notifyRecursive
+ pendingNotify = pendingNotify || changed;
+ if (!changed || !firstObserver)
+ return;
- dirty = false;
- return changed;
+ firstObserver.noSelfDependencies(this);
+ firstObserver.evaluateBindings();
+}
+
+void QPropertyBindingPrivate::notifyRecursive()
+{
+ if (!pendingNotify)
+ return;
+ pendingNotify = false;
+ Q_ASSERT(!updating);
+ updating = true;
+ if (firstObserver) {
+ firstObserver.noSelfDependencies(this);
+ firstObserver.notify(propertyDataPtr);
+ }
+ if (hasStaticObserver)
+ staticObserverCallback(propertyDataPtr);
+ updating = false;
}
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
@@ -232,7 +401,7 @@ QPropertyBindingData::~QPropertyBindingData()
observer.unlink();
observer = next;
}
- if (auto binding = d.bindingPtr())
+ if (auto binding = d.binding())
binding->unlinkAndDeref();
}
@@ -247,42 +416,38 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
QPropertyBindingDataPointer d{this};
QPropertyObserverPointer observer;
- if (auto *existingBinding = d.bindingPtr()) {
+ auto &data = d_ref();
+ if (auto *existingBinding = d.binding()) {
if (existingBinding == newBinding.data())
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
- if (existingBinding->isEagerlyUpdating()) {
+ if (existingBinding->isUpdating()) {
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
}
oldBinding = QPropertyBindingPrivatePtr(existingBinding);
observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
- d_ptr = 0;
+ data = 0;
} else {
observer = d.firstObserver();
}
if (newBinding) {
newBinding.data()->addRef();
- d_ptr = reinterpret_cast<quintptr>(newBinding.data());
- d_ptr |= BindingBit;
+ data = reinterpret_cast<quintptr>(newBinding.data());
+ data |= BindingBit;
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
- newBindingRaw->setDirty(true);
newBindingRaw->setProperty(propertyDataPtr);
if (observer)
newBindingRaw->prependObserver(observer);
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
- if (newBindingRaw->requiresEagerEvaluation()) {
- newBindingRaw->setEagerlyUpdating(true);
- auto changed = newBindingRaw->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr);
- if (changed)
- observer.notify(newBindingRaw, propertyDataPtr, /*knownToHaveChanged=*/true);
- newBindingRaw->setEagerlyUpdating(false);
- }
+
+ newBindingRaw->evaluateRecursive();
+ newBindingRaw->notifyRecursive();
} else if (observer) {
d.setObservers(observer.ptr);
} else {
- d_ptr &= ~QPropertyBindingData::BindingBit;
+ data = 0;
}
if (oldBinding)
@@ -293,8 +458,7 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(other.d_ptr, 0))
{
- QPropertyBindingDataPointer d{this};
- d.fixupFirstObserverAfterMove();
+ QPropertyBindingDataPointer::fixupAfterMove(this);
}
static thread_local QBindingStatus bindingStatus;
@@ -333,24 +497,23 @@ QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding()
return currentState ? currentState->binding : nullptr;
}
-void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *property) const
+// ### Unused, kept for BC with 6.0
+void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *) const
{
- QPropertyBindingDataPointer d{this};
- QPropertyBindingPrivate *binding = d.bindingPtr();
- if (!binding)
- return;
- binding->evaluateIfDirtyAndReturnTrueIfValueChanged(property);
}
void QPropertyBindingData::removeBinding_helper()
{
QPropertyBindingDataPointer d{this};
- auto *existingBinding = d.bindingPtr();
+ auto *existingBinding = d.binding();
Q_ASSERT(existingBinding);
+ if (existingBinding->isSticky()) {
+ return;
+ }
auto observer = existingBinding->takeObservers();
- d_ptr = 0;
+ d_ref() = 0;
if (observer)
d.setObservers(observer.ptr);
existingBinding->unlinkAndDeref();
@@ -370,22 +533,25 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
QPropertyBindingDataPointer d{this};
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
- dependencyObserver.setBindingToMarkDirty(currentState->binding);
+ dependencyObserver.setBindingToNotify(currentState->binding);
dependencyObserver.observeProperty(d);
}
void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const
{
+ if (isNotificationDelayed())
+ return;
QPropertyBindingDataPointer d{this};
- if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notify(d.bindingPtr(), propertyDataPtr);
-}
-
-void QPropertyBindingData::markDirty()
-{
- QPropertyBindingDataPointer d{this};
- if (auto *binding = d.bindingPtr())
- binding->setDirty(true);
+ QPropertyObserverPointer observer = d.firstObserver();
+ if (!observer)
+ return;
+ auto *delay = groupUpdateData;
+ if (delay) {
+ delay->addProperty(this, propertyDataPtr);
+ return;
+ }
+ observer.evaluateBindings();
+ observer.notify(propertyDataPtr);
}
int QPropertyBindingDataPointer::observerCount() const
@@ -425,7 +591,7 @@ QPropertyObserver::~QPropertyObserver()
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
{
- bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
+ binding = std::exchange(other.binding, {});
next = std::exchange(other.next, {});
prev = std::exchange(other.prev, {});
if (next)
@@ -441,9 +607,9 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
QPropertyObserverPointer d{this};
d.unlink();
- bindingToMarkDirty = nullptr;
+ binding = nullptr;
- bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
+ binding = std::exchange(other.binding, {});
next = std::exchange(other.next, {});
prev = std::exchange(other.prev, {});
if (next)
@@ -480,10 +646,10 @@ void QPropertyObserverPointer::setAliasedProperty(QUntypedPropertyData *property
ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
}
-void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding)
+void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
- ptr->bindingToMarkDirty = binding;
+ ptr->binding = binding;
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
}
@@ -516,14 +682,8 @@ struct [[nodiscard]] QPropertyObserverNodeProtector {
/*! \internal
\a propertyDataPtr is a pointer to the observed property's property data
- In case that property has a binding, \a triggeringBinding points to the binding's QPropertyBindingPrivate
- \a alreadyKnownToHaveChanged is an optional parameter, which is needed in the case
- of eager evaluation:
- There, we have already evaluated the binding, and thus the change detection for the
- ObserverNotifiesChangeHandler case would not work. Thus we instead pass the knowledge of
- whether the value has changed we obtained when evaluating the binding eagerly along
*/
-void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged)
+void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
{
auto observer = const_cast<QPropertyObserver*>(ptr);
/*
@@ -557,22 +717,17 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
observer = next->next.data();
continue;
}
- // both evaluateIfDirtyAndReturnTrueIfValueChanged and handlerToCall might modify the list
+ // handlerToCall might modify the list
QPropertyObserverNodeProtector protector(observer);
- if (!knownToHaveChanged && triggeringBinding) {
- if (!triggeringBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
- return;
- knownToHaveChanged = true;
- }
handlerToCall(observer, propertyDataPtr);
next = protector.next();
break;
}
case QPropertyObserver::ObserverNotifiesBinding:
{
- auto bindingToMarkDirty = observer->bindingToMarkDirty;
+ auto bindingToNotify = observer->binding;
QPropertyObserverNodeProtector protector(observer);
- bindingToMarkDirty->markDirtyAndNotifyObservers();
+ bindingToNotify->notifyRecursive();
next = protector.next();
break;
}
@@ -586,6 +741,39 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
}
}
+#ifndef QT_NO_DEBUG
+void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
+{
+ auto observer = const_cast<QPropertyObserver*>(ptr);
+ // See also comment in notify()
+ while (observer) {
+ if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding)
+ Q_ASSERT(observer->binding != binding);
+
+ observer = observer->next.data();
+ }
+
+}
+#endif
+
+void QPropertyObserverPointer::evaluateBindings()
+{
+ auto observer = const_cast<QPropertyObserver*>(ptr);
+ // See also comment in notify()
+ while (observer) {
+ QPropertyObserver *next = observer->next.data();
+
+ if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
+ auto bindingToEvaluate = observer->binding;
+ QPropertyObserverNodeProtector protector(observer);
+ bindingToEvaluate->evaluateRecursive();
+ next = protector.next();
+ }
+
+ observer = next;
+ }
+}
+
void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property)
{
if (ptr->prev)
@@ -723,21 +911,21 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn bool QUntypedBindable::isValid()
+ \fn bool QUntypedBindable::isValid() const
Returns true if the QUntypedBindable is valid. Methods called on an invalid
QUntypedBindable generally have no effect, unless otherwise noted.
*/
/*!
- \fn bool QUntypedBindable::isReadOnly()
+ \fn bool QUntypedBindable::isReadOnly() const
\since 6.1
Returns true if the QUntypedBindable is read-only.
*/
/*!
- \fn bool QUntypedBindable::isBindable()
+ \fn bool QUntypedBindable::isBindable() const
\internal
Returns true if the underlying property's binding can be queried
@@ -749,9 +937,9 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn QUntypedPropertyBinding QUntypedBindable::makeBinding()
+ \fn QUntypedPropertyBinding QUntypedBindable::makeBinding(const QPropertyBindingSourceLocation &location)
- Creates a binding returning the underlying properties' value.
+ Creates a binding returning the underlying properties' value, using a specified source \a location.
*/
/*!
@@ -771,15 +959,15 @@ QString QPropertyBindingError::description() const
*/
/*!
- template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f)
+ \fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f)
Behaves like a call to \a f followed by \c onValueChanged(f),
- \sa onValueChanged
+ \sa onValueChanged()
*/
/*!
- \fn QUntypedPropertyBinding QUntypedBindable::binding()
+ \fn QUntypedPropertyBinding QUntypedBindable::binding() const
Returns the underlying property's binding if there is any, or a default
constructed QUntypedPropertyBinding otherwise.
@@ -809,9 +997,9 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn bool QUntypedBindable::hasBinding()
+ \fn bool QUntypedBindable::hasBinding() const
- Returns true if the underlying property has a binding.
+ Returns \c true if the underlying property has a binding.
*/
/*!
@@ -834,22 +1022,24 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 0
\snippet code/src_corelib_kernel_qproperty.cpp 3
- \sa QMetaProperty::isBindable, template <typename T> QProperty<T>, QObjectBindableProperty
+ \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty
*/
/*!
\fn template<typename T> QPropertyBinding<T> QBindable<T>::makeBinding(const QPropertyBindingSourceLocation &location)
- Constructs a binding evaluating to the underlying property's value.
+ Constructs a binding evaluating to the underlying property's value, using a specified source
+ \a location.
*/
/*!
- \fn template <typename T> QPropertyBinding<T> QBindable<T>::binding()
+ \fn template <typename T> QPropertyBinding<T> QBindable<T>::binding() const
- Returns the currently set binding of the underlying property. If the property does not
+ Returns the currently set binding of the underlying property. If the property does not
have a binding, the returned \c QPropertyBinding<T> will be invalid.
- \sa setBinding, QPropertyBinding<T>::isValid(), hasBinding
+ \sa setBinding, hasBinding
+ //! \sa QPropertyBinding::isValid()
*/
/*!
@@ -858,7 +1048,8 @@ QString QPropertyBindingError::description() const
Removes the currently set binding of the underlying property and returns it.
If the property does not have a binding, the returned \c QPropertyBinding<T> will be invalid.
- \sa setBinding, getBinding, QPropertyBinding<T>::isValid(), hasBinding
+ \sa binding, setBinding, hasBinding
+ //! \sa QPropertyBinding::isValid()
*/
@@ -868,7 +1059,8 @@ QString QPropertyBindingError::description() const
Sets the underlying property's binding to \a binding. Does nothing if the QBindable is
read-only or invalid.
- \sa binding, QPropertyBinding<T>::isValid(), isReadOnly(), isValid()
+ \sa binding, isReadOnly(), isValid()
+ //! \sa QPropertyBinding::isValid()
*/
/*!
@@ -888,7 +1080,7 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> void QBindable<T>::setValue(const T & value) const
+ \fn template <typename T> void QBindable<T>::setValue(const T &value)
Sets the underlying property's value to \a value. This removes any currenltly set
binding from it. This function has no effect if the QBindable is read-only or invalid.
@@ -1038,6 +1230,8 @@ QString QPropertyBindingError::description() const
is read, the binding is evaluated by invoking the call operator () of \a f.
Whenever a dependency of the binding changes, the binding will be re-evaluated
the next time the value of this property is read.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1126,6 +1320,10 @@ QString QPropertyBindingError::description() const
In order to invoke the change signal on property changes, use
QObjectBindableProperty and pass the change signal as a callback.
+ A simple example is given in the following.
+
+ \snippet code/src_corelib_kernel_qproperty.cpp 4
+
QObjectBindableProperty is usually not used directly, instead an instance of it is created by
using the Q_OBJECT_BINDABLE_PROPERTY macro.
@@ -1153,6 +1351,185 @@ QString QPropertyBindingError::description() const
*/
/*!
+ \macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
+ \since 6.0
+ \relates QObjectBindableProperty
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass
+ of type \a type with name \a name. If the optional argument \a signal is given,
+ this signal will be emitted when the property is marked dirty.
+
+ \sa {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(containingClass, type, name, initialvalue, signal)
+ \since 6.0
+ \relates QObjectBindableProperty
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass
+ of type \a type with name \a name which is initialized to \a initialvalue.
+ If the optional argument \a signal is given, this signal will be emitted when
+ the property is marked dirty.
+
+ \sa {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \class QObjectCompatProperty
+ \inmodule QtCore
+ \brief The QObjectCompatProperty class is a template class to help port old
+ properties to the bindable property system.
+ \since 6.0
+ \ingroup tools
+ \internal
+
+ QObjectCompatProperty is a generic container that holds an
+ instance of \c T and behaves mostly like QProperty, just like
+ QObjectBindableProperty. It's one of the Qt internal classes implementing
+ \l {Qt Bindable Properties}. Like QObjectBindableProperty,
+ QObjectCompatProperty stores its management data structure in the surrounding
+ QObject. The last template parameter specifies a method (of the owning
+ class) to be called when the property is changed through the binding.
+ This is usually a setter.
+
+ As explained in \l {Qt Bindable Properties}, getters and setters for bindable
+ properties have to be almost trivial to be correct. However, in legacy code,
+ there is often complex logic in the setter. QObjectCompatProperty is a helper
+ to port these properties to the bindable property system.
+
+ With QObjectCompatProperty, the same rules as described in
+ \l {Bindable Property Getters and Setters} hold for the getter.
+ For the setter, the rules are different. It remains that every possible code
+ path in the setter must write to the underlying QObjectCompatProperty,
+ otherwise calling the setter might not remove a pre-existing binding, as
+ it should. However, as QObjectCompatProperty will call the setter on every
+ change, the setter is allowed to contain code like updating class internals
+ or emitting signals. Every write to the QObjectCompatProperty has to
+ be analyzed carefully to comply with the rules given in
+ \l {Writing to a Bindable Property}.
+
+ \sa Q_OBJECT_COMPAT_PROPERTY, QObjectBindableProperty, {Qt's Property System}, {Qt Bindable
+ Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPAT_PROPERTY(containingClass, type, name, callback)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectCompatProperty inside \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a setter function to be called when the property is changed through the binding.
+
+ \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(containingClass, type, name, callback, value)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectCompatProperty inside of \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a setter function to be called when the property is changed through the binding.
+ \a value specifies an initialization value.
+*/
+
+/*!
+ \class QObjectComputedProperty
+ \inmodule QtCore
+ \brief The QObjectComputedProperty class is a template class to help port old
+ properties to the bindable property system.
+ \since 6.0
+ \ingroup tools
+ \internal
+
+ QObjectComputedProperty is a read-only property which is recomputed on each read.
+ It does not store the computed value.
+ It is one of the Qt internal classes implementing \l {Qt Bindable Properties}.
+ QObjectComputedProperty is usually not used directly, instead an instance of it is created by
+ using the Q_OBJECT_COMPUTED_PROPERTY macro.
+
+ See the following example.
+
+ \code
+ class Client{};
+
+ class MyClassPrivate : public QObjectPrivate
+ {
+ public:
+ QList<Client> clients;
+ bool hasClientsActualCalculation() const { return clients.size() > 0; }
+ Q_OBJECT_COMPUTED_PROPERTY(MyClassPrivate, bool, hasClientsData,
+ &MyClassPrivate::hasClientsActualCalculation)
+ };
+
+ class MyClass : public QObject
+ {
+ // add q-object macro here (confuses qdoc if we do it here)
+ Q_PROPERTY(bool hasClients READ hasClients STORED false BINDABLE bindableHasClients)
+ public:
+ QBindable<bool> bindableHasClients()
+ {
+ return QBindable<bool>(&d_func()->hasClientsData);
+ }
+ bool hasClients() const
+ {
+ return d_func()->hasClientsData.value();
+ }
+ void addClient(const Client &c)
+ {
+ Q_D(MyClass);
+ d->clients.push_back(c);
+ // notify that the value could have changed
+ d->hasClientsData.markDirty();
+ }
+ private:
+ Q_DECLARE_PRIVATE(MyClass)
+ };
+ \endcode
+
+ The rules for getters in \l {Bindable Property Getters and Setters}
+ also apply for QObjectComputedProperty. Especially, the getter
+ should be trivial and only return the value of the QObjectComputedProperty object.
+ The callback given to the QObjectComputedProperty should usually be a private
+ method which is only called by the QObjectComputedProperty.
+
+ No setter is required or allowed, as QObjectComputedProperty is read-only.
+
+ To correctly participate in dependency handling, QObjectComputedProperty
+ has to know when its value, the result of the callback given to it, might
+ have changed. Whenever a bindable property used in the callback changes,
+ this happens automatically. If the result of the callback might change
+ because of a change in a value which is not a bindable property,
+ it is the developer's responsibility to call markDirty
+ on the QObjectComputedProperty object.
+ This will inform dependent properties about the potential change.
+
+ Note that calling markDirty might trigger change handlers in dependent
+ properties, which might in turn use the object the QObjectComputedProperty
+ is a member of. So markDirty must not be called when in a transitional
+ or invalid state.
+
+ QObjectComputedProperty is not suitable for use with a computation that depends
+ on any input that might change without notice, such as the contents of a file.
+
+ \sa Q_OBJECT_COMPUTED_PROPERTY, QObjectBindableProperty, {Qt's Property System},
+ {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectComputedProperty inside \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a GETTER function to be called when the property is evaluated.
+
+ \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty()
Constructs a property with a default constructed instance of T.
@@ -1254,6 +1631,8 @@ QString QPropertyBindingError::description() const
Whenever a dependency of the binding changes, the binding will be re-evaluated
the next time the value of this property is read. When the property value
changes, the owner is notified via the Callback function.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1480,6 +1859,8 @@ QString QPropertyBindingError::description() const
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1680,18 +2061,26 @@ QBindingStorage::~QBindingStorage()
QBindingStoragePrivate(d).destroy();
}
+// ### Unused, retained for BC with 6.0
void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
{
+ registerDependency_helper(data);
+}
+
+void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
+{
Q_ASSERT(bindingStatus);
+ // Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
+ // another thread do not register as dependencies
+ auto *currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
- auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ bindingStatus->currentlyEvaluatingBinding != nullptr);
+ auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ currentBinding != nullptr);
if (!storage)
return;
- if (auto *binding = storage->binding())
- binding->evaluateIfDirtyAndReturnTrueIfValueChanged(const_cast<QUntypedPropertyData *>(data), bindingStatus);
- storage->registerWithCurrentlyEvaluatingBinding(bindingStatus->currentlyEvaluatingBinding);
+ storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
}
+
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
{
return QBindingStoragePrivate(d).get(data);
@@ -1728,6 +2117,13 @@ bool isAnyBindingEvaluating()
{
return bindingStatus.currentlyEvaluatingBinding != nullptr;
}
+
+bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
+{
+ return bindingStatus.currentCompatProperty &&
+ bindingStatus.currentCompatProperty->property == property;
+}
+
} // namespace QtPrivate end
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index 6672f270f4..a6cb7ada48 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -63,6 +63,11 @@
QT_BEGIN_NAMESPACE
+namespace Qt {
+Q_CORE_EXPORT void beginPropertyUpdateGroup();
+Q_CORE_EXPORT void endPropertyUpdateGroup();
+}
+
template <typename T>
class QPropertyData : public QUntypedPropertyData
{
@@ -217,6 +222,7 @@ protected:
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
private:
+ friend struct QPropertyDelayedNotifications;
friend struct QPropertyObserverNodeProtector;
friend class QPropertyObserver;
friend struct QPropertyObserverPointer;
@@ -229,7 +235,7 @@ private:
QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
union {
- QPropertyBindingPrivate *bindingToMarkDirty = nullptr;
+ QPropertyBindingPrivate *binding = nullptr;
ChangeHandler changeHandler;
QUntypedPropertyData *aliasedPropertyData;
};
@@ -329,8 +335,6 @@ public:
parameter_type value() const
{
- if (d.hasBinding())
- d.evaluateIfDirty(this);
d.registerWithCurrentlyEvaluatingBinding();
return this->val;
}
@@ -389,9 +393,7 @@ public:
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
{
- QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this));
- notify();
- return oldBinding;
+ return QPropertyBinding<T>(d.setBinding(newBinding, this));
}
bool setBinding(const QUntypedPropertyBinding &newBinding)
@@ -402,11 +404,6 @@ public:
return true;
}
- void markDirty() {
- d.markDirty();
- notify();
- }
-
#ifndef Q_CLANG_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
@@ -577,7 +574,7 @@ public:
bool isBindable() const { return iface && iface->getBinding; }
bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); }
- QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
+ QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
{
return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
}
@@ -597,14 +594,14 @@ public:
return binding;
}
- void observe(QPropertyObserver *observer)
+ void observe(QPropertyObserver *observer) const
{
if (iface)
iface->setObserver(data, observer);
}
template<typename Functor>
- QPropertyChangeHandler<Functor> onValueChanged(Functor f)
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f) const
{
QPropertyChangeHandler<Functor> handler(f);
observe(&handler);
@@ -612,7 +609,7 @@ public:
}
template<typename Functor>
- QPropertyChangeHandler<Functor> subscribe(Functor f)
+ QPropertyChangeHandler<Functor> subscribe(Functor f) const
{
f();
return onValueChanged(f);
@@ -658,7 +655,7 @@ public:
}
}
- QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
+ QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
{
return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
}
@@ -844,7 +841,7 @@ class Q_CORE_EXPORT QBindingStorage
mutable QBindingStorageData *d = nullptr;
QBindingStatus *bindingStatus = nullptr;
- template<typename Class, typename T, auto Offset, auto Setter>
+ template<typename Class, typename T, auto Offset, auto Setter, auto Signal>
friend class QObjectCompatProperty;
public:
QBindingStorage();
@@ -852,11 +849,11 @@ public:
bool isEmpty() { return !d; }
- void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const
+ void registerDependency(const QUntypedPropertyData *data) const
{
- if (!d && !bindingStatus->currentlyEvaluatingBinding)
+ if (!bindingStatus->currentlyEvaluatingBinding)
return;
- maybeUpdateBindingAndRegister_helper(data);
+ registerDependency_helper(data);
}
QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
{
@@ -864,6 +861,9 @@ public:
return nullptr;
return bindingData_helper(data);
}
+ // ### Qt 7: remove unused BIC shim
+ void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(data); }
+
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
{
if (!d && !create)
@@ -871,6 +871,8 @@ public:
return bindingData_helper(data, create);
}
private:
+ void registerDependency_helper(const QUntypedPropertyData *data) const;
+ // ### Unused, but keep for BC
void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
@@ -923,7 +925,7 @@ public:
parameter_type value() const
{
- qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
+ qGetBindingStorage(owner())->registerDependency(this);
return this->val;
}
@@ -987,7 +989,6 @@ public:
{
QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
- notify(bd);
return static_cast<QPropertyBinding<T> &>(oldBinding);
}
@@ -1018,15 +1019,6 @@ public:
return bd && bd->binding() != nullptr;
}
- void markDirty() {
- QBindingStorage *storage = qGetBindingStorage(owner());
- auto bd = storage->bindingData(this, /*create=*/false);
- if (bd) { // if we have no BindingData, nobody can listen anyway
- bd->markDirty();
- notify(bd);
- }
- }
-
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -1130,7 +1122,7 @@ public:
parameter_type value() const
{
- qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
+ qGetBindingStorage(owner())->registerDependency(this);
return (owner()->*Getter)();
}
@@ -1176,15 +1168,13 @@ public:
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
}
- void markDirty() {
+ void notify() {
// computed property can't store a binding, so there's nothing to mark
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
if (bd)
- bindingData().notifyObservers(this);
+ bd->notifyObservers(this);
}
-
-private:
};
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index 015e9ea141..6a074efebb 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -45,8 +45,8 @@
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
//
// We mean it.
//
@@ -71,19 +71,18 @@ struct Q_AUTOTEST_EXPORT QPropertyBindingDataPointer
{
const QtPrivate::QPropertyBindingData *ptr = nullptr;
- QPropertyBindingPrivate *bindingPtr() const
+ QPropertyBindingPrivate *binding() const
{
- if (ptr->d_ptr & QtPrivate::QPropertyBindingData::BindingBit)
- return reinterpret_cast<QPropertyBindingPrivate*>(ptr->d_ptr - QtPrivate::QPropertyBindingData::BindingBit);
- return nullptr;
+ return ptr->binding();
}
void setObservers(QPropertyObserver *observer)
{
- observer->prev = reinterpret_cast<QPropertyObserver**>(&(ptr->d_ptr));
- ptr->d_ptr = reinterpret_cast<quintptr>(observer);
+ auto &d = ptr->d_ref();
+ observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
+ d = reinterpret_cast<quintptr>(observer);
}
- void fixupFirstObserverAfterMove() const;
+ static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr);
void addObserver(QPropertyObserver *observer);
void setFirstObserver(QPropertyObserver *observer);
QPropertyObserverPointer firstObserver() const;
@@ -104,11 +103,17 @@ struct QPropertyObserverPointer
void unlink();
- void setBindingToMarkDirty(QPropertyBindingPrivate *binding);
+ void setBindingToNotify(QPropertyBindingPrivate *binding);
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
void setAliasedProperty(QUntypedPropertyData *propertyPtr);
- void notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged = false);
+ void notify(QUntypedPropertyData *propertyDataPtr);
+#ifndef QT_NO_DEBUG
+ void noSelfDependencies(QPropertyBindingPrivate *binding);
+#else
+ void noSelfDependencies(QPropertyBindingPrivate *) {}
+#endif
+ void evaluateBindings();
void observeProperty(QPropertyBindingDataPointer property);
explicit operator bool() const { return ptr != nullptr; }
@@ -172,15 +177,18 @@ private:
private:
- // a dependent property has changed, and the binding needs to be reevaluated on access
- bool dirty = false;
// used to detect binding loops for lazy evaluated properties
bool updating = false;
bool hasStaticObserver = false;
+ bool pendingNotify = false;
bool hasBindingWrapper:1;
// used to detect binding loops for eagerly evaluated properties
- bool eagerlyUpdating:1;
bool isQQmlPropertyBinding:1;
+ /* a sticky binding does not get removed in removeBinding
+ this is used to support QQmlPropertyData::DontRemoveBinding
+ in qtdeclarative
+ */
+ bool m_sticky:1;
const QtPrivate::BindingFunctionVTable *vtable;
@@ -230,14 +238,15 @@ public:
// public because the auto-tests access it, too.
size_t dependencyObserverCount = 0;
- bool isEagerlyUpdating() {return eagerlyUpdating;}
- void setEagerlyUpdating(bool b) {eagerlyUpdating = b;}
+ bool isUpdating() {return updating;}
+ void setSticky(bool keep = true) {m_sticky = keep;}
+ bool isSticky() {return m_sticky;}
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
: hasBindingWrapper(false)
- , eagerlyUpdating(false)
, isQQmlPropertyBinding(isQQmlPropertyBinding)
+ , m_sticky(false)
, vtable(vtable)
, location(location)
, metaType(metaType)
@@ -245,7 +254,6 @@ public:
~QPropertyBindingPrivate();
- void setDirty(bool d) { dirty = d; }
void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
{
@@ -312,13 +320,8 @@ public:
void unlinkAndDeref();
- void markDirtyAndNotifyObservers();
- bool evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data, QBindingStatus *status = nullptr)
- {
- if (!dirty)
- return false;
- return evaluateIfDirtyAndReturnTrueIfValueChanged_helper(data, status);
- }
+ void evaluateRecursive();
+ void notifyRecursive();
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
@@ -334,8 +337,6 @@ public:
clearDependencyObservers();
}
- bool requiresEagerEvaluation() const { return hasBindingWrapper; }
-
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
bool hasCustomVTable() const
@@ -353,42 +354,51 @@ public:
delete[] reinterpret_cast<std::byte *>(priv);
}
}
-
-private:
- bool evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status = nullptr);
};
inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
{
- if (auto *binding = bindingPtr()) {
- binding->firstObserver.ptr = observer;
+ if (auto *b = binding()) {
+ b->firstObserver.ptr = observer;
return;
}
- ptr->d_ptr = reinterpret_cast<quintptr>(observer);
+ auto &d = ptr->d_ref();
+ d = reinterpret_cast<quintptr>(observer);
}
-inline void QPropertyBindingDataPointer::fixupFirstObserverAfterMove() const
+inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
{
+ auto &d = ptr->d_ref();
+ if (ptr->isNotificationDelayed()) {
+ QPropertyProxyBindingData *proxyData
+ = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit);
+ proxyData->originalBindingData = ptr;
+ }
// If QPropertyBindingData has been moved, and it has an observer
- // we have to adjust the firstObesrver's prev pointer to point to
+ // we have to adjust the firstObserver's prev pointer to point to
// the moved to QPropertyBindingData's d_ptr
- if (ptr->d_ptr & QtPrivate::QPropertyBindingData::BindingBit)
+ if (d & QtPrivate::QPropertyBindingData::BindingBit)
return; // nothing to do if the observer is stored in the binding
- if (auto observer = firstObserver())
- observer.ptr->prev = reinterpret_cast<QPropertyObserver **>(&(ptr->d_ptr));
+ if (auto observer = reinterpret_cast<QPropertyObserver *>(d))
+ observer->prev = reinterpret_cast<QPropertyObserver **>(&d);
}
inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() const
{
- if (auto *binding = bindingPtr())
- return binding->firstObserver;
- return { reinterpret_cast<QPropertyObserver *>(ptr->d_ptr) };
+ if (auto *b = binding())
+ return b->firstObserver;
+ return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
}
-template<typename Class, typename T, auto Offset, auto Setter>
+namespace QtPrivate {
+ Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
+}
+
+template<typename Class, typename T, auto Offset, auto Setter, auto Signal=nullptr>
class QObjectCompatProperty : public QPropertyData<T>
{
- using ThisType = QObjectCompatProperty<Class, T, Offset, Setter>;
+ using ThisType = QObjectCompatProperty<Class, T, Offset, Setter, Signal>;
+ using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
Class *owner()
{
char *that = reinterpret_cast<char *>(this);
@@ -413,10 +423,10 @@ class QObjectCompatProperty : public QPropertyData<T>
(thisData->owner()->*Setter)(copy.valueBypassingBindings());
return true;
}
- inline bool inBindingWrapper(const QBindingStorage *storage) const
+ bool inBindingWrapper(const QBindingStorage *storage) const
{
- return storage->bindingStatus->currentCompatProperty &&
- storage->bindingStatus->currentCompatProperty->property == this;
+ return storage->bindingStatus->currentCompatProperty
+ && QtPrivate::isPropertyInBindingWrapper(this);
}
public:
@@ -433,7 +443,7 @@ public:
const QBindingStorage *storage = qGetBindingStorage(owner());
// make sure we don't register this binding as a dependency to itself
if (!inBindingWrapper(storage))
- storage->maybeUpdateBindingAndRegister(this);
+ storage->registerDependency(this);
return this->val;
}
@@ -467,12 +477,13 @@ public:
const bool inWrapper = inBindingWrapper(storage);
if (bd && !inWrapper)
bd->removeBinding();
- if constexpr (QTypeTraits::has_operator_equal_v<T>)
- if (this->val == t)
- return;
this->val = t;
- if (!inWrapper)
- notify(bd);
+ }
+
+ QObjectCompatProperty &operator=(parameter_type newValue)
+ {
+ setValue(newValue);
+ return *this;
}
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
@@ -509,15 +520,6 @@ public:
return bd && bd->binding() != nullptr;
}
- void markDirty() {
- QBindingStorage *storage = qGetBindingStorage(owner());
- auto *bd = storage->bindingData(this, false);
- if (bd) {
- bd->markDirty();
- notify(bd);
- }
- }
-
void removeBindingUnlessInWrapper()
{
QBindingStorage *storage = qGetBindingStorage(owner());
@@ -528,6 +530,21 @@ public:
bd->removeBinding();
}
+ void notify()
+ {
+ QBindingStorage *storage = qGetBindingStorage(owner());
+ auto bd = storage->bindingData(this, false);
+ const bool inWrapper = inBindingWrapper(storage);
+ if (bd && !inWrapper)
+ notify(bd);
+ if constexpr (Signal != nullptr) {
+ if constexpr (SignalTakesValue::value)
+ (owner()->*Signal)(value());
+ else
+ (owner()->*Signal)();
+ }
+ }
+
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -559,6 +576,7 @@ public:
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
}
+
private:
void notify(const QtPrivate::QPropertyBindingData *binding)
{
@@ -567,24 +585,45 @@ private:
}
};
-#define Q_OBJECT_COMPAT_PROPERTY(Class, Type, name, setter) \
+#define Q_OBJECT_COMPAT_PROPERTY4(Class, Type, name, setter) \
static constexpr size_t _qt_property_##name##_offset() { \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
return offsetof(Class, name); \
- QT_WARNING_POP \
} \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name;
-#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(Class, Type, name, setter, value) \
- static constexpr size_t _qt_property_##name##_offset() \
- { \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \
- QT_WARNING_POP \
- } \
+#define Q_OBJECT_COMPAT_PROPERTY5(Class, Type, name, setter, signal) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name;
+
+#define Q_OBJECT_COMPAT_PROPERTY(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(Q_OBJECT_COMPAT_PROPERTY, __VA_ARGS__) \
+ QT_WARNING_POP
+
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS5(Class, Type, name, setter, value) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name = \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter>( \
value);
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS6(Class, Type, name, setter, signal, value) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name = \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
+ signal>(value);
+
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__) \
+ QT_WARNING_POP
+
+
namespace QtPrivate {
Q_CORE_EXPORT BindingEvaluationState *suspendCurrentBindingStatus();
Q_CORE_EXPORT void restoreBindingStatus(BindingEvaluationState *status);
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index 474e911995..f59221c4df 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -44,9 +44,9 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
@@ -143,7 +143,6 @@ private:
QtPrivate::RefCounted *d;
};
-
class QUntypedPropertyBinding;
class QPropertyBindingPrivate;
struct QPropertyBindingDataPointer;
@@ -158,6 +157,24 @@ public:
template <typename T>
class QPropertyData;
+// Used for grouped property evaluations
+namespace QtPrivate {
+class QPropertyBindingData;
+}
+struct QPropertyDelayedNotifications;
+struct QPropertyProxyBindingData
+{
+ // acts as QPropertyBindingData::d_ptr
+ quintptr d_ptr;
+ /*
+ The two members below store the original binding data and property
+ data pointer of the property which gets proxied.
+ They are set in QPropertyDelayedNotifications::addProperty
+ */
+ const QtPrivate::QPropertyBindingData *originalBindingData;
+ QUntypedPropertyData *propertyData;
+};
+
namespace QtPrivate {
struct BindingEvaluationState;
@@ -218,6 +235,16 @@ struct QPropertyBindingFunction {
using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction);
+/*!
+ \internal
+ A property normally consists of the actual property value and metadata for the binding system.
+ QPropertyBindingData is the latter part. It stores a pointer to either
+ - a (potentially empty) linked list of notifiers, in case there is no binding set,
+ - an actual QUntypedPropertyBinding when the property has a binding,
+ - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update.
+
+ \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup
+ */
class Q_CORE_EXPORT QPropertyBindingData
{
// Mutable because the address of the observer of the currently evaluating binding is stored here, for
@@ -225,6 +252,7 @@ class Q_CORE_EXPORT QPropertyBindingData
mutable quintptr d_ptr = 0;
friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
+ friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications);
Q_DISABLE_COPY(QPropertyBindingData)
public:
QPropertyBindingData() = default;
@@ -232,9 +260,13 @@ public:
QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete;
~QPropertyBindingData();
- static inline constexpr quintptr BindingBit = 0x1; // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
+ // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
+ static inline constexpr quintptr BindingBit = 0x1;
+ // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers?
+ static inline constexpr quintptr DelayedNotificationBit = 0x2;
bool hasBinding() const { return d_ptr & BindingBit; }
+ bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; }
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
QUntypedPropertyData *propertyDataPtr,
@@ -243,14 +275,14 @@ public:
QPropertyBindingPrivate *binding() const
{
- if (d_ptr & BindingBit)
- return reinterpret_cast<QPropertyBindingPrivate*>(d_ptr - BindingBit);
+ quintptr dd = d();
+ if (dd & BindingBit)
+ return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit);
return nullptr;
}
- void evaluateIfDirty(const QUntypedPropertyData *property) const;
- void markDirty();
+ void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused
void removeBinding()
{
@@ -267,6 +299,25 @@ public:
void registerWithCurrentlyEvaluatingBinding() const;
void notifyObservers(QUntypedPropertyData *propertyDataPtr) const;
private:
+ /*!
+ \internal
+ Returns a reference to d_ptr, except when d_ptr points to a proxy.
+ In that case, a reference to proxy->d_ptr is returned instead.
+
+ To properly support proxying, direct access to d_ptr only occcurs when
+ - a function actually deals with proxying (e.g.
+ QPropertyDelayedNotifications::addProperty),
+ - only the tag value is accessed (e.g. hasBinding) or
+ - inside a constructor.
+ */
+ quintptr &d_ref() const
+ {
+ quintptr &d = d_ptr;
+ if (isNotificationDelayed())
+ return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit))->d_ptr;
+ return d;
+ }
+ quintptr d() const { return d_ref(); }
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
void removeBinding_helper();
};
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index 49c57cb6ee..10e26fcdef 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -241,7 +241,7 @@ void QTimer::start()
stop();
d->nulltimer = (!d->inter && d->single);
d->id = QObject::startTimer(d->inter, d->type);
- d->isActiveData.markDirty();
+ d->isActiveData.notify();
}
/*!
@@ -257,10 +257,11 @@ void QTimer::start()
void QTimer::start(int msec)
{
Q_D(QTimer);
- d->inter.removeBindingUnlessInWrapper();
- d->inter.setValueBypassingBindings(msec);
+ const bool intervalChanged = msec != d->inter;
+ d->inter.setValue(msec);
start();
- d->inter.markDirty();
+ if (intervalChanged)
+ d->inter.notify();
}
@@ -277,7 +278,7 @@ void QTimer::stop()
if (d->id != INV_TIMER) {
QObject::killTimer(d->id);
d->id = INV_TIMER;
- d->isActiveData.markDirty();
+ d->isActiveData.notify();
}
}
@@ -754,17 +755,16 @@ QBindable<bool> QTimer::bindableSingleShot()
void QTimer::setInterval(int msec)
{
Q_D(QTimer);
- d->inter.removeBindingUnlessInWrapper();
-
- d->inter.setValueBypassingBindings(msec);
+ const bool intervalChanged = msec != d->inter;
+ d->inter.setValue(msec);
if (d->id != INV_TIMER) { // create new timer
QObject::killTimer(d->id); // restart timer
d->id = QObject::startTimer(msec, d->type);
// No need to call markDirty() for d->isActiveData here,
// as timer state actually does not change
}
-
- d->inter.markDirty();
+ if (intervalChanged)
+ d->inter.notify();
}
int QTimer::interval() const
diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp
index bcaaf7af05..db4d4b1188 100644
--- a/src/corelib/kernel/qtimerinfo_unix.cpp
+++ b/src/corelib/kernel/qtimerinfo_unix.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -631,13 +631,16 @@ int QTimerInfoList::activateTimers()
if (currentTimerInfo->interval > 0)
n_act++;
+ // Send event, but don't allow it to recurse:
if (!currentTimerInfo->activateRef) {
- // send event, but don't allow it to recurse
currentTimerInfo->activateRef = &currentTimerInfo;
QTimerEvent e(currentTimerInfo->id);
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
+ // Storing currentTimerInfo's address in its activateRef allows the
+ // handling of that event to clear this local variable on deletion
+ // of the object it points to - if it didn't, clear activateRef:
if (currentTimerInfo)
currentTimerInfo->activateRef = nullptr;
}
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index d95d3cd0fb..9ebdbc4a18 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -333,7 +333,7 @@ class Q_CORE_EXPORT QVariant
explicit QVariant(Type type)
: QVariant(QMetaType(int(type)))
{}
- QT_DEPRECATED_VERSION_X_6_0("Use metaType().")
+ QT_DEPRECATED_VERSION_X_6_0("Use typeId() or metaType().")
Type type() const
{
int type = d.typeId();
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index f9e91e73d7..3371e101b6 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -117,10 +117,7 @@ QWinEventNotifier::QWinEventNotifier(QObject *parent)
QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent)
: QObject(*new QWinEventNotifierPrivate(hEvent, false), parent)
{
- Q_D(QWinEventNotifier);
-
- d->registerWaitObject();
- d->enabled = true;
+ setEnabled(true);
}
/*!
@@ -198,9 +195,20 @@ void QWinEventNotifier::setEnabled(bool enable)
// event shall be ignored.
d->winEventActPosted.testAndSetRelaxed(QWinEventNotifierPrivate::Posted,
QWinEventNotifierPrivate::IgnorePosted);
- d->registerWaitObject();
- } else if (d->waitHandle != NULL) {
- d->unregisterWaitObject();
+ // The notifier can't be registered, if 'enabled' flag was false.
+ // The code in the else branch ensures that.
+ Q_ASSERT(!d->registered);
+ SetThreadpoolWait(d->waitObject, d->handleToEvent, NULL);
+ d->registered = true;
+ } else if (d->registered) {
+ // Stop waiting for an event. However, there may be a callback queued
+ // already after the call.
+ SetThreadpoolWait(d->waitObject, NULL, NULL);
+ // So, to avoid a race condition after a possible call to
+ // setEnabled(true), wait for a possibly outstanding callback
+ // to complete.
+ WaitForThreadpoolWaitCallbacks(d->waitObject, TRUE);
+ d->registered = false;
}
}
@@ -226,12 +234,16 @@ bool QWinEventNotifier::event(QEvent * e)
// again.
if (d->winEventActPosted.fetchAndStoreRelaxed(QWinEventNotifierPrivate::NotPosted)
== QWinEventNotifierPrivate::Posted && d->enabled) {
- d->unregisterWaitObject();
+ // Clear the flag, as the wait object is implicitly unregistered
+ // when the callback is queued.
+ d->registered = false;
emit activated(d->handleToEvent, QPrivateSignal());
- if (d->enabled && d->waitHandle == NULL)
- d->registerWaitObject();
+ if (d->enabled && !d->registered) {
+ SetThreadpoolWait(d->waitObject, d->handleToEvent, NULL);
+ d->registered = true;
+ }
}
return true;
default:
@@ -240,8 +252,25 @@ bool QWinEventNotifier::event(QEvent * e)
return QObject::event(e);
}
-void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ignore*/)
+QWinEventNotifierPrivate::QWinEventNotifierPrivate(HANDLE h, bool e)
+ : handleToEvent(h), enabled(e), registered(false)
+{
+ waitObject = CreateThreadpoolWait(waitCallback, this, NULL);
+ if (waitObject == NULL)
+ qErrnoWarning("QWinEventNotifier:: CreateThreadpollWait failed.");
+}
+
+QWinEventNotifierPrivate::~QWinEventNotifierPrivate()
+{
+ CloseThreadpoolWait(waitObject);
+}
+
+void QWinEventNotifierPrivate::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult)
{
+ Q_UNUSED(instance);
+ Q_UNUSED(wait);
+ Q_UNUSED(waitResult);
QWinEventNotifierPrivate *nd = reinterpret_cast<QWinEventNotifierPrivate *>(context);
// Do not post an event, if an event is already in the message queue. Note
@@ -252,23 +281,4 @@ void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ig
}
}
-bool QWinEventNotifierPrivate::registerWaitObject()
-{
- if (RegisterWaitForSingleObject(&waitHandle, handleToEvent, wfsoCallback, this,
- INFINITE, WT_EXECUTEONLYONCE) == 0) {
- qErrnoWarning("QWinEventNotifier: RegisterWaitForSingleObject failed.");
- return false;
- }
- return true;
-}
-
-void QWinEventNotifierPrivate::unregisterWaitObject()
-{
- // Unregister the wait handle and wait for pending callbacks to finish.
- if (UnregisterWaitEx(waitHandle, INVALID_HANDLE_VALUE))
- waitHandle = NULL;
- else
- qErrnoWarning("QWinEventNotifier: UnregisterWaitEx failed.");
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h
index da9d8ff9a0..4e402ec826 100644
--- a/src/corelib/kernel/qwineventnotifier_p.h
+++ b/src/corelib/kernel/qwineventnotifier_p.h
@@ -63,21 +63,20 @@ class QWinEventNotifierPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QWinEventNotifier)
public:
- QWinEventNotifierPrivate()
- : handleToEvent(0), enabled(false) {}
- QWinEventNotifierPrivate(HANDLE h, bool e)
- : handleToEvent(h), enabled(e) {}
+ QWinEventNotifierPrivate() : QWinEventNotifierPrivate(0, false) {}
+ QWinEventNotifierPrivate(HANDLE h, bool e);
+ virtual ~QWinEventNotifierPrivate();
- static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/);
- bool registerWaitObject();
- void unregisterWaitObject();
+ static void CALLBACK waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult);
HANDLE handleToEvent;
- HANDLE waitHandle = NULL;
+ PTP_WAIT waitObject = NULL;
enum PostingState { NotPosted = 0, Posted, IgnorePosted };
QAtomicInt winEventActPosted;
bool enabled;
+ bool registered;
};
QT_END_NAMESPACE