diff options
Diffstat (limited to 'src/corelib')
| -rw-r--r-- | src/corelib/Qt6AndroidMacros.cmake | 21 | ||||
| -rw-r--r-- | src/corelib/configure.cmake | 9 | ||||
| -rw-r--r-- | src/corelib/doc/src/cmake/qt_add_android_dynamic_feature_java_source_dirs.qdoc | 30 | ||||
| -rw-r--r-- | src/corelib/doc/src/cmake/qt_add_android_permission.qdoc | 4 | ||||
| -rw-r--r-- | src/corelib/global/qcompilerdetection.h | 2 | ||||
| -rw-r--r-- | src/corelib/itemmodels/qrangemodel_impl.h | 41 | ||||
| -rw-r--r-- | src/corelib/kernel/qobject.cpp | 35 | ||||
| -rw-r--r-- | src/corelib/kernel/qpermissions.cpp | 6 | ||||
| -rw-r--r-- | src/corelib/platform/wasm/qstdweb.cpp | 113 | ||||
| -rw-r--r-- | src/corelib/platform/wasm/qstdweb_p.h | 81 |
10 files changed, 299 insertions, 43 deletions
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake index 6a83e947146..be362ba1925 100644 --- a/src/corelib/Qt6AndroidMacros.cmake +++ b/src/corelib/Qt6AndroidMacros.cmake @@ -106,6 +106,27 @@ function(qt6_add_android_dynamic_features target) endif() endfunction() + +function(qt_add_android_dynamic_feature_java_source_dirs) + qt6_add_android_dynamic_feature_java_source_dirs(${ARGV}) +endfunction() + +# Add java source directories for dynamic feature. Intermediate solution until java library +# support exists. +function(qt6_add_android_dynamic_feature_java_source_dirs target) + + set(opt_args "") + set(single_args "") + set(multi_args + SOURCE_DIRS + ) + cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}") + if(arg_SOURCE_DIRS) + set_property(TARGET ${target} APPEND PROPERTY + _qt_android_gradle_java_source_dirs ${arg_SOURCE_DIRS}) + endif() +endfunction() + # Generate the deployment settings json file for a cmake target. function(qt6_android_generate_deployment_settings target) # Information extracted from mkspecs/features/android/android_deployment_settings.prf diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index edcfba0f6ce..08908082991 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -31,7 +31,10 @@ qt_find_package_extend_sbom(TARGETS GLIB2::GLIB2 LICENSE_EXPRESSION "LGPL-2.1-or-later" ) qt_find_package(ICU 50.1 COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU::uc ICU::data - MODULE_NAME core QMAKE_LIB icu) + MODULE_NAME core QMAKE_LIB icu + VCPKG_PORT icu + VCPKG_PLATFORM !windows +) if(QT_FEATURE_dlopen) qt_add_qmake_lib_dependency(icu libdl) @@ -49,7 +52,9 @@ qt_find_package_extend_sbom(TARGETS Libb2::Libb2 qt_find_package(WrapRt MODULE PROVIDED_TARGETS WrapRt::WrapRt MODULE_NAME core QMAKE_LIB librt) qt_find_package(WrapSystemPCRE2 10.20 MODULE - PROVIDED_TARGETS WrapSystemPCRE2::WrapSystemPCRE2 MODULE_NAME core QMAKE_LIB pcre2) + PROVIDED_TARGETS WrapSystemPCRE2::WrapSystemPCRE2 MODULE_NAME core QMAKE_LIB pcre2 + VCPKG_PORT pcre2 +) set_package_properties(WrapPCRE2 PROPERTIES TYPE REQUIRED) if((QNX) OR QT_FIND_ALL_PACKAGES_ALWAYS) qt_find_package(PPS MODULE PROVIDED_TARGETS PPS::PPS MODULE_NAME core QMAKE_LIB pps) diff --git a/src/corelib/doc/src/cmake/qt_add_android_dynamic_feature_java_source_dirs.qdoc b/src/corelib/doc/src/cmake/qt_add_android_dynamic_feature_java_source_dirs.qdoc new file mode 100644 index 00000000000..cf670110cab --- /dev/null +++ b/src/corelib/doc/src/cmake/qt_add_android_dynamic_feature_java_source_dirs.qdoc @@ -0,0 +1,30 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! +\page qt_add_android_dynamic_feature_java_source_dirs.html +\ingroup cmake-commands-qtcore + +\title qt_add_android_dynamic_feature_java_source_dirs +\keyword qt6_add_android_dynamic_feature_java_source_dirs + +\summary {Adds Java source directories to a dynamic feature build.} + +\include cmake-find-package-core.qdocinc + +\cmakecommandsince 6.11 + +\section1 Synopsis + +\badcode +qt_add_android_dynamic_feature_java_source_dirs(target [SOURCE_DIRS <directory1> <directory2> ...]) +\endcode + +\versionlessCMakeCommandsNote qt6_add_android_dynamic_feature_java_source_dirs() + +\section1 Description + +The command adds extra Java/Kotlin source directories to the \c {target} +executable when building the executable app with dynamic feature functionality. +To be used in conjunction with qt6_add_android_dynamic_features(). +*/ diff --git a/src/corelib/doc/src/cmake/qt_add_android_permission.qdoc b/src/corelib/doc/src/cmake/qt_add_android_permission.qdoc index 68fe6a8e5cc..8fa6c2bb6d7 100644 --- a/src/corelib/doc/src/cmake/qt_add_android_permission.qdoc +++ b/src/corelib/doc/src/cmake/qt_add_android_permission.qdoc @@ -14,6 +14,10 @@ \cmakecommandsince 6.9 +\note When using this API, the \c{<!-- %%INSERT_PERMISSIONS -->} tag must be present +in the AndroidManifest.xml. For further information on the use of this tag, +see \l {Qt Permissions and Features} + \section1 Synopsis \badcode diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 7bb2e5b04ef..0b42af7686c 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -1446,7 +1446,7 @@ QT_WARNING_DISABLE_MSVC(4355) /* 'this' : used in base member initializer list * QT_WARNING_DISABLE_MSVC(4710) /* function not inlined */ QT_WARNING_DISABLE_MSVC(4530) /* C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc */ # elif defined(Q_CC_CLANG_ONLY) -# if Q_CC_CLANG >= 2100 +# if __has_warning("-Wcharacter-conversion") QT_WARNING_DISABLE_CLANG("-Wcharacter-conversion") /* until https://github.com/llvm/llvm-project/issues/163719 is fixed */ # endif # elif defined(Q_CC_BOR) diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h index 70552cbfe05..88bd6cf444e 100644 --- a/src/corelib/itemmodels/qrangemodel_impl.h +++ b/src/corelib/itemmodels/qrangemodel_impl.h @@ -28,7 +28,7 @@ #include <functional> #include <iterator> #include <type_traits> -#include <QtCore/q20type_traits.h> +#include <QtCore/qxptype_traits.h> #include <tuple> #include <QtCore/q23utility.h> @@ -562,35 +562,30 @@ namespace QRangeModelDetails } }; - template <typename P, typename R, typename = void> - struct protocol_parentRow : std::false_type {}; template <typename P, typename R> - struct protocol_parentRow<P, R, - std::void_t<decltype(std::declval<P&>().parentRow(std::declval<wrapped_t<R>&>()))>> - : std::true_type {}; + using protocol_parentRow_test = decltype(std::declval<P&>() + .parentRow(std::declval<QRangeModelDetails::wrapped_t<R>&>())); + template <typename P, typename R> + using protocol_parentRow = qxp::is_detected<protocol_parentRow_test, P, R>; - template <typename P, typename R, typename = void> - struct protocol_childRows : std::false_type {}; template <typename P, typename R> - struct protocol_childRows<P, R, - std::void_t<decltype(std::declval<P&>().childRows(std::declval<wrapped_t<R>&>()))>> - : std::true_type {}; + using protocol_childRows_test = decltype(std::declval<P&>() + .childRows(std::declval<QRangeModelDetails::wrapped_t<R>&>())); + template <typename P, typename R> + using protocol_childRows = qxp::is_detected<protocol_childRows_test, P, R>; - template <typename P, typename R, typename = void> - struct protocol_setParentRow : std::false_type {}; template <typename P, typename R> - struct protocol_setParentRow<P, R, - std::void_t<decltype(std::declval<P&>().setParentRow(std::declval<wrapped_t<R>&>(), - std::declval<wrapped_t<R>*>()))>> - : std::true_type {}; + using protocol_setParentRow_test = decltype(std::declval<P&>() + .setParentRow(std::declval<QRangeModelDetails::wrapped_t<R>&>(), + std::declval<QRangeModelDetails::wrapped_t<R>*>())); + template <typename P, typename R> + using protocol_setParentRow = qxp::is_detected<protocol_setParentRow_test, P, R>; - template <typename P, typename R, typename = void> - struct protocol_mutable_childRows : std::false_type {}; template <typename P, typename R> - struct protocol_mutable_childRows<P, R, - std::void_t<decltype(refTo(std::declval<P&>().childRows(std::declval<wrapped_t<R>&>())) - = {}) >> - : std::true_type {}; + using protocol_mutable_childRows_test = decltype(refTo(std::declval<P&>() + .childRows(std::declval<wrapped_t<R>&>())) = {}); + template <typename P, typename R> + using protocol_mutable_childRows = qxp::is_detected<protocol_mutable_childRows_test, P, R>; template <typename P, typename = void> struct protocol_newRow : std::false_type {}; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 02c9f00f301..607dc23f56c 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2696,23 +2696,38 @@ static void err_method_notfound(const QObject *object, case QSIGNAL_CODE: type = "signal"; break; } const char *loc = extract_location(method); + const char *err; if (strchr(method, ')') == nullptr) // common typing mistake - qCWarning(lcConnect, "QObject::%s: Parentheses expected, %s %s::%s%s%s", func, type, - object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : ""); + err = "Parentheses expected,"; else - qCWarning(lcConnect, "QObject::%s: No such %s %s::%s%s%s", func, type, - object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : ""); + err = "No such"; + qCWarning(lcConnect, "QObject::%s: %s %s %s::%s%s%s", func, err, type, + object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : ""); +} + +enum class ConnectionEnd : bool { Sender, Receiver }; +Q_DECL_COLD_FUNCTION +static void err_info_about_object(const char *func, const QObject *o, ConnectionEnd end) +{ + if (!o) + return; + const QString name = o->objectName(); + if (name.isEmpty()) + return; + const bool sender = end == ConnectionEnd::Sender; + qCWarning(lcConnect, "QObject::%s: (%s name:%*s'%ls')", + func, + sender ? "sender" : "receiver", + sender ? 3 : 1, // ← length of generated whitespace + "", + qUtf16Printable(name)); } Q_DECL_COLD_FUNCTION static void err_info_about_objects(const char *func, const QObject *sender, const QObject *receiver) { - QString a = sender ? sender->objectName() : QString(); - QString b = receiver ? receiver->objectName() : QString(); - if (!a.isEmpty()) - qCWarning(lcConnect, "QObject::%s: (sender name: '%s')", func, a.toLocal8Bit().data()); - if (!b.isEmpty()) - qCWarning(lcConnect, "QObject::%s: (receiver name: '%s')", func, b.toLocal8Bit().data()); + err_info_about_object(func, sender, ConnectionEnd::Sender); + err_info_about_object(func, receiver, ConnectionEnd::Receiver); } Q_DECL_COLD_FUNCTION diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp index bbcea8338ca..6767917e176 100644 --- a/src/corelib/kernel/qpermissions.cpp +++ b/src/corelib/kernel/qpermissions.cpp @@ -124,7 +124,11 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg); The relevant permission names are described in the documentation for each permission type. - \sa {Qt Creator: Editing Manifest Files} + \note When using this API, the \c{<!-- %%INSERT_PERMISSIONS -->} tag must be present in + the AndroidManifest.xml. For further information on the use of this tag, + see \l {Qt Permissions and Features} + + \sa {Qt Creator: Editing Manifest Files}. \section1 Available Permissions diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp index 4f3ecc4c6d9..bbaf2c442a0 100644 --- a/src/corelib/platform/wasm/qstdweb.cpp +++ b/src/corelib/platform/wasm/qstdweb.cpp @@ -446,6 +446,119 @@ EventCallback::EventCallback(emscripten::val element, const std::string &name, } +size_t qstdweb::Promise::State::s_numInstances = 0; + +// +// When a promise settles, all attached handlers will be called in +// the order they where added. +// +// In particular a finally handler will be called according to its +// position in the call chain. Which is not necessarily at the end, +// +// This makes cleanup difficult. If we cleanup to early, we will remove +// handlers before they have a chance to be called. This would be the +// case if we add a finally handler in the Promise constructor. +// +// For correct cleanup it is necessary that it happens after the +// last handler has been called. +// +// We choose to implement this by making sure the last handler +// is always a finally handler. +// +// In this case we have multiple finally handlers, so any called +// handler checks if it is the last handler to be called. +// If it is, the cleanup is performed, otherwise cleanup +// is delayed to the last handler. +// +// We could try to let the handlers cleanup themselves, but this +// only works for finally handlers. A then or catch handler is not +// necessarily called, and would not cleanup itself. +// +// We could try to let a (then,catch) pair cleanup both handlers, +// under the assumption that one of them will always be called. +// This does not work in the case that we do not have both handlers, +// or if the then handler throws (both should be called in this case). +// +Promise& Promise::addThenFunction(std::function<void(emscripten::val)> thenFunc) +{ + QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get(); + Q_ASSERT(suspendResume); + + m_state->m_handlers.push_back(suspendResume->registerEventHandler(thenFunc)); + m_state->m_promise = + m_state->m_promise.call<emscripten::val>( + "then", + suspendResume->jsEventHandlerAt( + m_state->m_handlers.back())); + + addFinallyFunction([](){}); // Add a potential cleanup handler + return (*this); +} + +Promise& Promise::addCatchFunction(std::function<void(emscripten::val)> catchFunc) +{ + QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get(); + Q_ASSERT(suspendResume); + + m_state->m_handlers.push_back(suspendResume->registerEventHandler(catchFunc)); + m_state->m_promise = + m_state->m_promise.call<emscripten::val>( + "catch", + suspendResume->jsEventHandlerAt( + m_state->m_handlers.back())); + + addFinallyFunction([](){}); // Add a potential cleanup handler + return (*this); +} + +Promise& Promise::addFinallyFunction(std::function<void()> finallyFunc) +{ + QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get(); + Q_ASSERT(suspendResume); + + auto thisHandler = std::make_shared<uint32_t>((uint32_t)(-1)); + auto state = m_state; + + std::function<void(emscripten::val)> func = + [state, thisHandler, finallyFunc](emscripten::val element) { + Q_UNUSED(element); + + finallyFunc(); + + // See comment at top, we can only do the cleanup + // if we are the last handler in the handler chain + if (state->m_handlers.back() == *thisHandler) { + auto guard = state; // removeEventHandler will remove also this function + QWasmSuspendResumeControl *suspendResume = QWasmSuspendResumeControl::get(); + Q_ASSERT(suspendResume); + for (int i = 0; i < guard->m_handlers.size(); ++i) { + suspendResume->removeEventHandler(guard->m_handlers[i]); + guard->m_handlers[i] = (uint32_t)(-1); + } + } + }; + + *thisHandler = suspendResume->registerEventHandler(func); + m_state->m_handlers.push_back(*thisHandler); + m_state->m_promise = + m_state->m_promise.call<emscripten::val>( + "finally", + suspendResume->jsEventHandlerAt( + m_state->m_handlers.back())); + + return (*this); +} + +void Promise::suspendExclusive() +{ + Promise::suspendExclusive(m_state->m_handlers); +} + +emscripten::val Promise::getPromise() const +{ + return m_state->m_promise; +} + uint32_t Promise::adoptPromise(emscripten::val promise, PromiseCallbacks callbacks, QList<uint32_t> *handlers) { Q_ASSERT_X(!!callbacks.catchFunc || !!callbacks.finallyFunc || !!callbacks.thenFunc, diff --git a/src/corelib/platform/wasm/qstdweb_p.h b/src/corelib/platform/wasm/qstdweb_p.h index b14d9e4012f..07df021c444 100644 --- a/src/corelib/platform/wasm/qstdweb_p.h +++ b/src/corelib/platform/wasm/qstdweb_p.h @@ -237,11 +237,80 @@ namespace qstdweb { std::function<void()> finallyFunc; }; - namespace Promise { - uint32_t Q_CORE_EXPORT adoptPromise(emscripten::val promise, PromiseCallbacks callbacks, QList<uint32_t> *handlers = nullptr); + // Note: it is ok for the Promise object to go out of scope, + // the resources will be cleaned up in the finally handler. + class Q_CORE_EXPORT Promise { + public: + template<typename... Args> + Promise(emscripten::val target, QString methodName, Args... args) { + m_state = std::make_shared<State>(); + m_state->m_promise = target.call<emscripten::val>( + methodName.toStdString().c_str(), std::forward<Args>(args)...); + if (m_state->m_promise.isUndefined() || m_state->m_promise["constructor"]["name"].as<std::string>() != "Promise") { + qFatal("This function did not return a promise"); + } + addFinallyFunction([](){}); + } + + Promise(emscripten::val promise) { + m_state = std::make_shared<State>(); + m_state->m_promise = promise; + if (m_state->m_promise.isUndefined() || m_state->m_promise["constructor"]["name"].as<std::string>() != "Promise") { + qFatal("This function did not return a promise"); + } + addFinallyFunction([](){}); + } + + Promise(const std::vector<Promise> &promises) { + std::vector<emscripten::val> all; + all.reserve(promises.size()); + for (const auto &p : promises) + all.push_back(p.getPromise()); + + auto arr = emscripten::val::array(all); + m_state = std::make_shared<State>(); + m_state->m_promise = emscripten::val::global("Promise").call<emscripten::val>("all", arr); + addFinallyFunction([](){}); + } + + Promise& addThenFunction(std::function<void(emscripten::val)> thenFunc); + Promise& addCatchFunction(std::function<void(emscripten::val)> catchFunc); + Promise& addFinallyFunction(std::function<void()> finallyFunc); + + void suspendExclusive(); + + emscripten::val getPromise() const; + + public: + class State { + private: + friend class Promise; + + State(const State&) = delete; + State(State&&) = delete; + State& operator=(const State&) = delete; + State& operator=(State&&) = delete; + + public: + State() { ++s_numInstances; } + ~State() { --s_numInstances; } + static size_t numInstances() { return s_numInstances; } + + private: + emscripten::val m_promise = emscripten::val::undefined(); + QList<uint32_t> m_handlers; + static size_t s_numInstances; + }; + + private: + std::shared_ptr<State> m_state; + + public: + // Deprecated: To be backwards compatible + static uint32_t Q_CORE_EXPORT adoptPromise(emscripten::val promise, PromiseCallbacks callbacks, QList<uint32_t> *handlers = nullptr); template<typename... Args> - uint32_t make(emscripten::val target, + static uint32_t make(emscripten::val target, QString methodName, PromiseCallbacks callbacks, Args... args) @@ -256,7 +325,7 @@ namespace qstdweb { } template<typename... Args> - void make( + static void make( QList<uint32_t> &handlers, emscripten::val target, QString methodName, @@ -272,8 +341,8 @@ namespace qstdweb { adoptPromise(std::move(promiseObject), std::move(callbacks), &handlers); } - void Q_CORE_EXPORT suspendExclusive(QList<uint32_t> handlerIndices); - void Q_CORE_EXPORT all(std::vector<emscripten::val> promises, PromiseCallbacks callbacks); + static void Q_CORE_EXPORT suspendExclusive(QList<uint32_t> handlerIndices); + static void Q_CORE_EXPORT all(std::vector<emscripten::val> promises, PromiseCallbacks callbacks); }; template<class F> |
