Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcore_ohos_p.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QCORE_OHOS_P_H
5#define QCORE_OHOS_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/private/qnapi_p.h>
19#include <QtCore/private/qohoscommon_p.h>
20#include <QtCore/qglobal.h>
21#include <QtCore/qobject.h>
22#include <QtCore/qpointer.h>
23#include <QtCore/qmutex.h>
24#include <algorithm>
25#include <cstdint>
26#include <functional>
27#include <map>
28#include <memory>
29#include <optional>
30#include <pthread.h>
31#include <string>
32#include <tuple>
33#include <type_traits>
34#include <typeinfo>
35#include <vector>
36
37QT_BEGIN_NAMESPACE
38
39namespace QtOhos {
40
41class QObjectThreadSafeRef;
42
43template<typename Enum>
45{
46};
47
48}
49
50class Q_CORE_EXPORT QOhosJsState
51{
52public:
53 QOhosJsState(const QOhosJsState &) = delete;
54 QOhosJsState &operator=(const QOhosJsState &) = delete;
55
56 virtual ~QOhosJsState();
57
58 virtual napi_env env() = 0;
59
60 virtual QNapi::Object defaultWindowStageOrEmpty() = 0;
61 virtual QNapi::Object defaultUiContextOrEmpty() = 0;
62
63 virtual std::optional<QNapi::Object> tryGetJsWindowByQWindow(QtOhos::QObjectThreadSafeRef qwindow) = 0;
64 virtual std::optional<QNapi::Object> tryGetQAbilityByQWindow(QtOhos::QObjectThreadSafeRef qwindow) = 0;
65 virtual std::optional<QNapi::Object> defaultQAbility() = 0;
66
67 virtual QNapi::Object appLaunchWant() = 0;
68 virtual std::optional<QNapi::Object> optAppLaunchParam() = 0;
69
70 // a registered module, or an unknown one loaded with napi_load_module() and memoized; empty if that load fails
71 virtual std::optional<QNapi::Object> tryGetModule(const std::string &moduleName) = 0;
72
73 template<typename T>
74 std::enable_if_t<std::is_default_constructible<T>::value, T> &getAttachedObjectWithLazyCreate();
75
76 virtual void startAppProcess(
77 const std::string &processId, QNapi::Object requestWant,
78 QNapi::Object optStartOptions, std::function<void(QOhosJsState &)> continueFunc) = 0;
79
80 virtual void startNoUiChildProcess(const std::string &libraryName, const std::vector<std::string> &args) = 0;
81
82 // wantConsumer's parameters: jsState, qAbility, want, launchParam
83 [[nodiscard]] virtual std::shared_ptr<void> registerNewWantConsumer(
84 QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object> wantConsumer) = 0;
85
86 // requestsHandler's parameters: jsState, wantParams, resultConsumer
87 virtual void setOnContinueRequestsHandler(
88 QNapi::Object qAbility,
89 std::function<void(QOhosJsState &, QNapi::Object, QOhosConsumer<QOhosJsState &, QNapi::Number>)> requestsHandler) = 0;
90
91 virtual void setDestroyFromSystemAllowed(QNapi::Object qAbility, bool destroyAllowed) = 0;
92
93 template<typename T = QNapi::Value>
94 T eval(const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs = {});
95
96 QNapi::Promise evalToPromiseOrRejectOnThrow(
97 const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs = {});
98
99 template<typename Enum>
100 QNapi::Number mapOhosEnumToJs(Enum enumValue);
101
102 template<typename Enum>
103 std::optional<Enum> tryMapOhosEnumFromJs(QNapi::Number enumJsValue);
104
105 template<typename Enum>
106 Enum mapOhosEnumFromJs(QNapi::Number enumJsValue);
107
108protected:
109 struct OhosEnumInfo
110 {
111 std::string fullTypeName;
112 std::vector<std::pair<int, const char *>> enumeratorsNames;
113 };
114
115 QOhosJsState();
116
117private:
118 template<typename Enum, typename = void>
119 struct OhosEnumFullTypeNameFetcher
120 {
121 static std::string fullTypeName()
122 {
123 return QtOhos::OhosEnumMeta<Enum>::moduleName + std::string(".") + QtOhos::OhosEnumMeta<Enum>::typeName;
124 }
125 };
126
127 template<typename Enum>
128 struct OhosEnumFullTypeNameFetcher<Enum, decltype(static_cast<void>(&QtOhos::OhosEnumMeta<Enum>::fullTypeName))>
129 {
130 static std::string fullTypeName()
131 {
132 return QtOhos::OhosEnumMeta<Enum>::fullTypeName;
133 }
134 };
135
136 template<typename Enum>
137 static OhosEnumInfo makeOhosEnumInfo();
138
139 virtual std::tuple<QNapi::Object, std::string> extractModuleFromEvalExpr(const std::string &expr) = 0;
140 virtual void *getAttachedObjectWithLazyCreate(
141 const std::type_info &objectTypeInfo, QOhosSupplier<std::shared_ptr<void>> objectFactory) = 0;
142 virtual QNapi::Number mapOhosEnumToJs(int enumValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) = 0;
143 virtual std::optional<int> tryMapOhosEnumFromJs(QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) = 0;
144 virtual int mapOhosEnumFromJs(QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) = 0;
145};
146
147class Q_CORE_EXPORT QOhosCallbackInfo : public QNapi::CallbackInfo
148{
149public:
150 using QNapi::CallbackInfo::CallbackInfo;
151
152 QOhosJsState &jsState() const;
153};
154
156
157Q_CORE_EXPORT void invoke(std::function<void(QOhosJsState &)> &&task);
158
159Q_CORE_EXPORT void invokeAndWaitForContinue(
160 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
161 std::string callerContextName = {});
162
163Q_CORE_EXPORT void runAndWait(
164 const std::function<void(QOhosJsState &)> &task,
165 std::string callerContextName = {});
166
167template<typename Func>
168auto eval(Func &&func, std::string callerContextName = {})
169 -> decltype(func(std::declval<QOhosJsState &>()));
170
171template<typename T>
173 std::function<void(QOhosJsState &, QOhosTaskPromise<T>)> evalFunc,
174 std::string callerContextName = {});
175
176// Backward-compatible alias for evalWithPromise(); kept so existing callers
177// passing a result consumer keep compiling.
178template<typename T>
179T evalWithConsumer(std::function<void(QOhosJsState &, std::function<void(T)>)> evalFunc);
180
181}
182
183class Q_CORE_EXPORT QOhosJsThreadOps
184{
185public:
186 virtual ~QOhosJsThreadOps();
187
188 virtual QOhosJsState &jsState() = 0;
189
190 virtual void invoke(std::function<void(QOhosJsState &)> &&task) = 0;
191 virtual void invokeAndWaitForContinue(
192 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
193 std::string callerContextName) = 0;
194 virtual void runAndWait(
195 const std::function<void(QOhosJsState &)> &task,
196 std::string callerContextName) = 0;
197
198 static void registerInstance(QOhosJsThreadOps *ops);
199 static QOhosJsThreadOps &instance();
200
201protected:
202 QOhosJsThreadOps();
203};
204
205namespace QtOhos {
206
207class Q_CORE_EXPORT QObjectThreadSafeRef
208{
209public:
212
215
216 bool operator==(const QObjectThreadSafeRef &other) const;
217 bool operator!=(const QObjectThreadSafeRef &other) const;
218
219 std::string refName() const;
220
221 // it may be called only inside Qt thread that created the reference
222 QPointer<QObject> data() const;
223
224 void visitInQtThreadIfAlive(std::function<void(QObject &)> &&visitFunc) const;
225
226private:
227 struct QObjectRef
228 {
231 };
232
234 Q_CONSTINIT inline static std::uint64_t refsMapInsertCounter = 0;
235
239
240public:
242};
243
244template<typename T>
246{
247public:
248 static_assert(std::is_base_of<QObject, T>::value, "The class supports QObject subtypes only");
249
251 QThreadSafeRef(QPointer<T> obj);
252
255
256 QObjectThreadSafeRef toQObjectThreadSafeRef() const;
257
258 std::string refName() const;
259
260 // it may be called only inside Qt thread that created the reference
261 QPointer<T> data() const;
262
263 void visitInQtThreadIfAlive(std::function<void(T &)> &&visitFunc) const;
264
265private:
266 QObjectThreadSafeRef m_ref;
267};
268
270{
271public:
272 QtState(const QtState &) = delete;
273 QtState &operator=(const QtState &) = delete;
274
275 virtual ~QtState();
276
277 virtual bool isQtThread() const = 0;
278
279 virtual void invokeTask(std::function<void()> &&task) = 0;
280
281protected:
283};
284
285template<typename T>
287
288// this function should be called once from Qt thread at some point during startup
289Q_CORE_EXPORT void initQtThreadState();
290
291// Creates a proxy for the base shared_ptr which uses custom deleter to ensure
292// that the underlying shared_ptr is destroyed (synchronously) in the JS thread.
293// This ensures that the managed object will be also destroyed in the JS thread,
294// as long as the caller passes the only existing shared_ptr instance managing
295// that object to the function. This is the caller's responsibility to do this
296// (preferably by using a call to some std::shared_ptr<T> factory function as
297// the function's argument).
298template<typename T>
299std::shared_ptr<T> makeProxyWithJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr);
300
301// as makeProxyWithJsThreadDeleter(), but the release is posted to the JS thread instead of awaited
302template<typename T>
303std::shared_ptr<T> makeProxyWithAsyncJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr);
304
305// invokes the task inside the Qt thread, can be called from any thread at any time
306Q_CORE_EXPORT void invokeInQtThread(std::function<void()> &&task);
307
308Q_CORE_EXPORT QtState &getQtState();
309
310// logs error message with supplied prefix and error details extracted from
311// JS error object passed as an argument via CallbackInfo
312Q_CORE_EXPORT void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix);
313
314// Creates JS callback that expects JS error object as a JS argument
315// and logs details of the error. The logged message includes provided
316// call context string (usually the name of called method).
318
319template<typename T>
321{
322 return QThreadSafeRef<T>(obj);
323}
324
325template<typename T>
326QThreadSafeRef<T>::QThreadSafeRef() = default;
327
328template<typename T>
329QThreadSafeRef<T>::QThreadSafeRef(QPointer<T> obj)
330 : QObjectThreadSafeRef(obj.data())
331{
332}
333
334template<typename T>
335QThreadSafeRef<T>::QThreadSafeRef(const QThreadSafeRef<T> &other) = default;
336
337template<typename T>
338QThreadSafeRef<T> &QThreadSafeRef<T>::operator=(const QThreadSafeRef<T> &other) = default;
339
340template<typename T>
341std::string QThreadSafeRef<T>::refName() const
342{
343 return QObjectThreadSafeRef::refName();
344}
345
346template<typename T>
347QObjectThreadSafeRef QThreadSafeRef<T>::toQObjectThreadSafeRef() const
348{
349 return static_cast<const QObjectThreadSafeRef &>(*this);
350}
351
352template<typename T>
354{
355 return static_cast<T *>(
356 static_cast<QObject *>(
357 QObjectThreadSafeRef::data()));
358}
359
360template<typename T>
361void QThreadSafeRef<T>::visitInQtThreadIfAlive(std::function<void(T &)> &&visitFunc) const
362{
363 QObjectThreadSafeRef::visitInQtThreadIfAlive(
364 [sharedVisitFunc = takeFunctionToSharedPtr(visitFunc)](QObject &obj) {
365 const auto takenVisitFunc = std::exchange(*sharedVisitFunc, nullptr);
366 takenVisitFunc(static_cast<T &>(obj));
367 });
368}
369
370template<typename T>
371std::shared_ptr<T> makeProxyWithJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr)
372{
373 auto *baseRawPtr = baseSharedPtr.get();
374 return std::shared_ptr<T>(
375 baseRawPtr,
376 [baseSharedPtr = std::move(baseSharedPtr)](T *) mutable {
378 [&](QOhosJsState &) {
379 baseSharedPtr.reset();
380 },
381 Q_FUNC_INFO);
382 });
383}
384
385template<typename T>
386std::shared_ptr<T> makeProxyWithAsyncJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr)
387{
388 auto *baseRawPtr = baseSharedPtr.get();
389 return std::shared_ptr<T>(
390 baseRawPtr,
391 [baseSharedPtr = std::move(baseSharedPtr)](T *) mutable {
393 [baseSharedPtr = std::move(baseSharedPtr)](QOhosJsState &) mutable {
394 baseSharedPtr.reset();
395 });
396 });
397}
398
399}
400
401template<typename T>
402T QOhosJsState::eval(const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs)
403{
404 using namespace std::string_literals;
405
406 QNapi::Object module;
407 std::string subExpr;
408 std::tie(module, subExpr) = extractModuleFromEvalExpr(expr);
409
410 return !subExpr.empty()
411 ? module.eval<T>(subExpr, exprArgs)
412 : QNapi::checkedCast<T>(
413 module,
414 [&]() {
415 return "module '"s + expr + "'"s;
416 });
417}
418
419inline QNapi::Promise QOhosJsState::evalToPromiseOrRejectOnThrow(
420 const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs)
421{
422 QNapi::Object module;
423 std::string subExpr;
424 std::tie(module, subExpr) = extractModuleFromEvalExpr(expr);
425
426 return module.evalToPromiseOrRejectOnThrow(subExpr, exprArgs);
427}
428
429template<typename T>
430std::enable_if_t<std::is_default_constructible<T>::value, T> &QOhosJsState::getAttachedObjectWithLazyCreate()
431{
432 auto *objectPtr = reinterpret_cast<T *>(
433 getAttachedObjectWithLazyCreate(typeid(T), &std::make_shared<T>));
434 return *objectPtr;
435}
436
437template<typename Enum>
438QNapi::Number QOhosJsState::mapOhosEnumToJs(Enum enumValue)
439{
440 return mapOhosEnumToJs(static_cast<int>(enumValue), typeid(Enum), &makeOhosEnumInfo<Enum>);
441}
442
443template<typename Enum>
444std::optional<Enum> QOhosJsState::tryMapOhosEnumFromJs(QNapi::Number enumJsValue)
445{
446 auto intValue = tryMapOhosEnumFromJs(enumJsValue, typeid(Enum), &makeOhosEnumInfo<Enum>);
447 return intValue.has_value()
448 ? std::optional(static_cast<Enum>(intValue.value()))
449 : std::nullopt;
450}
451
452template<typename Enum>
453Enum QOhosJsState::mapOhosEnumFromJs(QNapi::Number enumJsValue)
454{
455 return static_cast<Enum>(mapOhosEnumFromJs(enumJsValue, typeid(Enum), &makeOhosEnumInfo<Enum>));
456}
457
458template<typename Enum>
459QOhosJsState::OhosEnumInfo QOhosJsState::makeOhosEnumInfo()
460{
461 static const auto enumEnumeratorsNames = QtOhos::OhosEnumMeta<Enum>::enumeratorsNames;
462
463 std::vector<std::pair<int, const char *>> intEnumeratorsNames;
464 std::transform(
465 enumEnumeratorsNames.begin(), enumEnumeratorsNames.end(),
466 std::back_inserter(intEnumeratorsNames),
467 [](const auto &valueNamePair) {
468 return std::pair<int, const char *>(static_cast<int>(valueNamePair.first), valueNamePair.second);
469 });
470
471 return OhosEnumInfo {
472 .fullTypeName = OhosEnumFullTypeNameFetcher<Enum>::fullTypeName(),
473 .enumeratorsNames = std::move(intEnumeratorsNames),
474 };
475}
476
477template<typename Func>
478auto QOhosJsThreadGateway::eval(Func &&func, std::string callerContextName)
479 -> decltype(func(std::declval<QOhosJsState &>()))
480{
481 using Result = decltype(func(std::declval<QOhosJsState &>()));
482 static_assert(
483 !(std::is_class<Result>::value
484 && (
485 std::is_convertible<Result, napi_value>::value
486 || std::is_convertible<Result, napi_ref>::value)),
487 "NAPI values/references must not be accessed outside the JS thread");
488
489 std::unique_ptr<Result> result;
490 runAndWait(
491 [&](QOhosJsState &jsState) {
492 result = std::make_unique<Result>(func(jsState));
493 },
494 std::move(callerContextName));
495 return std::move(*result);
496}
497
498template<typename T>
500 std::function<void(QOhosJsState &, QOhosTaskPromise<T>)> evalFunc,
501 std::string callerContextName)
502{
503 static_assert(
504 !(std::is_class<T>::value
505 && (
506 std::is_convertible<T, napi_value>::value
507 || std::is_convertible<T, napi_ref>::value)),
508 "NAPI values/references must not be accessed outside the JS thread");
509
510 std::unique_ptr<T> result;
511 invokeAndWaitForContinue(
512 [&](QOhosJsState &jsState, QOhosTaskPromise<> taskPromise) {
513 evalFunc(
514 jsState,
515 QOhosTaskPromise<T>(
516 [&result, taskPromise = std::move(taskPromise)](T value) {
517 result = std::make_unique<T>(std::move(value));
518 taskPromise();
519 },
520 {},
521 std::move(callerContextName)));
522 },
523 callerContextName);
524
525 return std::move(*result);
526}
527
528template<typename T>
529T QOhosJsThreadGateway::evalWithConsumer(std::function<void(QOhosJsState &, std::function<void(T)>)> evalFunc)
530{
531 return evalWithPromise<T>(
532 [evalFunc = std::move(evalFunc)](QOhosJsState &jsState, QOhosTaskPromise<T> resultPromise) {
533 evalFunc(jsState, std::move(resultPromise));
534 });
535}
536
537QT_END_NAMESPACE
538
539#endif
QPointer< T > data() const
QThreadSafeRef(const QThreadSafeRef< T > &other)
std::string refName() const
void visitInQtThreadIfAlive(std::function< void(T &)> &&visitFunc) const
QThreadSafeRef & operator=(const QThreadSafeRef< T > &other)
QObjectThreadSafeRef toQObjectThreadSafeRef() const
QtState & operator=(const QtState &)=delete
virtual void invokeTask(std::function< void()> &&task)=0
QtState(const QtState &)=delete
virtual bool isQtThread() const =0
virtual ~QtState()
T evalWithConsumer(std::function< void(QOhosJsState &, std::function< void(T)>)> evalFunc)
T evalWithPromise(std::function< void(QOhosJsState &, QOhosTaskPromise< T >)> evalFunc, std::string callerContextName={})
Q_CORE_EXPORT void invoke(std::function< void(QOhosJsState &)> &&task)
auto eval(Func &&func, std::string callerContextName={}) -> decltype(func(std::declval< QOhosJsState & >()))
Q_CORE_EXPORT void runAndWait(const std::function< void(QOhosJsState &)> &task, std::string callerContextName={})
Q_CORE_EXPORT void invokeAndWaitForContinue(std::function< void(QOhosJsState &, QOhosTaskPromise<>)> &&task, std::string callerContextName={})
void initQtThreadState()
QThreadSafeRef< T > makeQThreadSafeRef(T *obj)
std::shared_ptr< T > makeProxyWithJsThreadDeleter(std::shared_ptr< T > &&baseSharedPtr)
void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix)
QtState & getQtState()
std::shared_ptr< T > makeProxyWithAsyncJsThreadDeleter(std::shared_ptr< T > &&baseSharedPtr)
std::function< void(const QOhosCallbackInfo &)> makeErrorLoggingJsCallback(std::string callContext)
void invokeInQtThread(std::function< void()> &&task)
static QOhosJsThreadOps * qOhosJsThreadOpsInstance