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{
157 std::optional<std::string> optName;
158 std::optional<std::string> optMessage;
159 std::optional<std::int64_t> optCode;
160};
161
163
164Q_CORE_EXPORT void invoke(std::function<void(QOhosJsState &)> &&task);
165
166Q_CORE_EXPORT void invokeAndWaitForContinue(
167 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
168 std::string callerContextName = {});
169
170Q_CORE_EXPORT void runAndWait(
171 const std::function<void(QOhosJsState &)> &task,
172 std::string callerContextName = {});
173
174template<typename Func>
175auto eval(Func &&func, std::string callerContextName = {})
176 -> decltype(func(std::declval<QOhosJsState &>()));
177
178template<typename T>
180 std::function<void(QOhosJsState &, QOhosTaskPromise<T>)> evalFunc,
181 std::string callerContextName = {});
182
183}
184
185class Q_CORE_EXPORT QOhosJsThreadOps
186{
187public:
188 virtual ~QOhosJsThreadOps();
189
190 virtual QOhosJsState &jsState() = 0;
191
192 virtual void invoke(std::function<void(QOhosJsState &)> &&task) = 0;
193 virtual void invokeAndWaitForContinue(
194 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
195 std::string callerContextName) = 0;
196 virtual void runAndWait(
197 const std::function<void(QOhosJsState &)> &task,
198 std::string callerContextName) = 0;
199
200 static void registerInstance(QOhosJsThreadOps *ops);
201 static QOhosJsThreadOps &instance();
202
203protected:
204 QOhosJsThreadOps();
205};
206
207namespace QtOhos {
208
209class Q_CORE_EXPORT QObjectThreadSafeRef
210{
211public:
214
217
218 bool operator==(const QObjectThreadSafeRef &other) const;
219 bool operator!=(const QObjectThreadSafeRef &other) const;
220
221 std::string refName() const;
222
223 // it may be called only inside Qt thread that created the reference
224 QPointer<QObject> data() const;
225
226 void visitInQtThreadIfAlive(std::function<void(QObject &)> &&visitFunc) const;
227
228private:
229 struct QObjectRef
230 {
233 };
234
236 Q_CONSTINIT inline static std::uint64_t refsMapInsertCounter = 0;
237
241
242public:
244};
245
246template<typename T>
248{
249public:
250 static_assert(std::is_base_of<QObject, T>::value, "The class supports QObject subtypes only");
251
253 QThreadSafeRef(QPointer<T> obj);
254
257
258 QObjectThreadSafeRef toQObjectThreadSafeRef() const;
259
260 std::string refName() const;
261
262 // it may be called only inside Qt thread that created the reference
263 QPointer<T> data() const;
264
265 void visitInQtThreadIfAlive(std::function<void(T &)> &&visitFunc) const;
266
267private:
268 QObjectThreadSafeRef m_ref;
269};
270
272{
273public:
274 QtState(const QtState &) = delete;
275 QtState &operator=(const QtState &) = delete;
276
277 virtual ~QtState();
278
279 virtual bool isQtThread() const = 0;
280
281 virtual void invokeTask(std::function<void()> &&task) = 0;
282
283protected:
285};
286
287template<typename T>
289
290// this function should be called once from Qt thread at some point during startup
291Q_CORE_EXPORT void initQtThreadState();
292
293// Creates a proxy for the base shared_ptr which uses custom deleter to ensure
294// that the underlying shared_ptr is destroyed (synchronously) in the JS thread.
295// This ensures that the managed object will be also destroyed in the JS thread,
296// as long as the caller passes the only existing shared_ptr instance managing
297// that object to the function. This is the caller's responsibility to do this
298// (preferably by using a call to some std::shared_ptr<T> factory function as
299// the function's argument).
300template<typename T>
301std::shared_ptr<T> makeProxyWithJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr);
302
303// as makeProxyWithJsThreadDeleter(), but the release is posted to the JS thread instead of awaited
304template<typename T>
305std::shared_ptr<T> makeProxyWithAsyncJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr);
306
307// invokes the task inside the Qt thread, can be called from any thread at any time
308Q_CORE_EXPORT void invokeInQtThread(std::function<void()> &&task);
309
310Q_CORE_EXPORT QtState &getQtState();
311
312// logs error message with supplied prefix and error details extracted from
313// JS error object passed as an argument via CallbackInfo
314Q_CORE_EXPORT void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix);
315
316// reads the string name and message and the integer code (a number or an integer string) of a JS error, where present
317Q_CORE_EXPORT QOhosJsErrorInfo tryExtractErrorInfoFromJsError(QNapi::Value jsErrorOrEmpty);
318
319// Creates JS callback that expects JS error object as a JS argument
320// and logs details of the error. The logged message includes provided
321// call context string (usually the name of called method).
323
324template<typename T>
326{
327 return QThreadSafeRef<T>(obj);
328}
329
330template<typename T>
331QThreadSafeRef<T>::QThreadSafeRef() = default;
332
333template<typename T>
334QThreadSafeRef<T>::QThreadSafeRef(QPointer<T> obj)
335 : QObjectThreadSafeRef(obj.data())
336{
337}
338
339template<typename T>
340QThreadSafeRef<T>::QThreadSafeRef(const QThreadSafeRef<T> &other) = default;
341
342template<typename T>
343QThreadSafeRef<T> &QThreadSafeRef<T>::operator=(const QThreadSafeRef<T> &other) = default;
344
345template<typename T>
346std::string QThreadSafeRef<T>::refName() const
347{
348 return QObjectThreadSafeRef::refName();
349}
350
351template<typename T>
352QObjectThreadSafeRef QThreadSafeRef<T>::toQObjectThreadSafeRef() const
353{
354 return static_cast<const QObjectThreadSafeRef &>(*this);
355}
356
357template<typename T>
359{
360 return static_cast<T *>(
361 static_cast<QObject *>(
362 QObjectThreadSafeRef::data()));
363}
364
365template<typename T>
366void QThreadSafeRef<T>::visitInQtThreadIfAlive(std::function<void(T &)> &&visitFunc) const
367{
368 QObjectThreadSafeRef::visitInQtThreadIfAlive(
369 [sharedVisitFunc = takeFunctionToSharedPtr(visitFunc)](QObject &obj) {
370 const auto takenVisitFunc = std::exchange(*sharedVisitFunc, nullptr);
371 takenVisitFunc(static_cast<T &>(obj));
372 });
373}
374
375template<typename T>
376std::shared_ptr<T> makeProxyWithJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr)
377{
378 auto *baseRawPtr = baseSharedPtr.get();
379 return std::shared_ptr<T>(
380 baseRawPtr,
381 [baseSharedPtr = std::move(baseSharedPtr)](T *) mutable {
383 [&](QOhosJsState &) {
384 baseSharedPtr.reset();
385 },
386 Q_FUNC_INFO);
387 });
388}
389
390template<typename T>
391std::shared_ptr<T> makeProxyWithAsyncJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr)
392{
393 auto *baseRawPtr = baseSharedPtr.get();
394 return std::shared_ptr<T>(
395 baseRawPtr,
396 [baseSharedPtr = std::move(baseSharedPtr)](T *) mutable {
398 [baseSharedPtr = std::move(baseSharedPtr)](QOhosJsState &) mutable {
399 baseSharedPtr.reset();
400 });
401 });
402}
403
404}
405
406template<typename T>
407T QOhosJsState::eval(const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs)
408{
409 using namespace std::string_literals;
410
411 QNapi::Object module;
412 std::string subExpr;
413 std::tie(module, subExpr) = extractModuleFromEvalExpr(expr);
414
415 return !subExpr.empty()
416 ? module.eval<T>(subExpr, exprArgs)
417 : QNapi::checkedCast<T>(
418 module,
419 [&]() {
420 return "module '"s + expr + "'"s;
421 });
422}
423
424inline QNapi::Promise QOhosJsState::evalToPromiseOrRejectOnThrow(
425 const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs)
426{
427 QNapi::Object module;
428 std::string subExpr;
429 std::tie(module, subExpr) = extractModuleFromEvalExpr(expr);
430
431 return module.evalToPromiseOrRejectOnThrow(subExpr, exprArgs);
432}
433
434template<typename T>
435std::enable_if_t<std::is_default_constructible<T>::value, T> &QOhosJsState::getAttachedObjectWithLazyCreate()
436{
437 auto *objectPtr = reinterpret_cast<T *>(
438 getAttachedObjectWithLazyCreate(typeid(T), &std::make_shared<T>));
439 return *objectPtr;
440}
441
442template<typename Enum>
443QNapi::Number QOhosJsState::mapOhosEnumToJs(Enum enumValue)
444{
445 return mapOhosEnumToJs(static_cast<int>(enumValue), typeid(Enum), &makeOhosEnumInfo<Enum>);
446}
447
448template<typename Enum>
449std::optional<Enum> QOhosJsState::tryMapOhosEnumFromJs(QNapi::Number enumJsValue)
450{
451 auto intValue = tryMapOhosEnumFromJs(enumJsValue, typeid(Enum), &makeOhosEnumInfo<Enum>);
452 return intValue.has_value()
453 ? std::optional(static_cast<Enum>(intValue.value()))
454 : std::nullopt;
455}
456
457template<typename Enum>
458Enum QOhosJsState::mapOhosEnumFromJs(QNapi::Number enumJsValue)
459{
460 return static_cast<Enum>(mapOhosEnumFromJs(enumJsValue, typeid(Enum), &makeOhosEnumInfo<Enum>));
461}
462
463template<typename Enum>
464QOhosJsState::OhosEnumInfo QOhosJsState::makeOhosEnumInfo()
465{
466 static const auto enumEnumeratorsNames = QtOhos::OhosEnumMeta<Enum>::enumeratorsNames;
467
468 std::vector<std::pair<int, const char *>> intEnumeratorsNames;
469 std::transform(
470 enumEnumeratorsNames.begin(), enumEnumeratorsNames.end(),
471 std::back_inserter(intEnumeratorsNames),
472 [](const auto &valueNamePair) {
473 return std::pair<int, const char *>(static_cast<int>(valueNamePair.first), valueNamePair.second);
474 });
475
476 return OhosEnumInfo {
477 .fullTypeName = OhosEnumFullTypeNameFetcher<Enum>::fullTypeName(),
478 .enumeratorsNames = std::move(intEnumeratorsNames),
479 };
480}
481
482template<typename Func>
483auto QOhosJsThreadGateway::eval(Func &&func, std::string callerContextName)
484 -> decltype(func(std::declval<QOhosJsState &>()))
485{
486 using Result = decltype(func(std::declval<QOhosJsState &>()));
487 static_assert(
488 !(std::is_class<Result>::value
489 && (
490 std::is_convertible<Result, napi_value>::value
491 || std::is_convertible<Result, napi_ref>::value)),
492 "NAPI values/references must not be accessed outside the JS thread");
493
494 std::unique_ptr<Result> result;
495 runAndWait(
496 [&](QOhosJsState &jsState) {
497 result = std::make_unique<Result>(func(jsState));
498 },
499 std::move(callerContextName));
500 return std::move(*result);
501}
502
503template<typename T>
505 std::function<void(QOhosJsState &, QOhosTaskPromise<T>)> evalFunc,
506 std::string callerContextName)
507{
508 static_assert(
509 !(std::is_class<T>::value
510 && (
511 std::is_convertible<T, napi_value>::value
512 || std::is_convertible<T, napi_ref>::value)),
513 "NAPI values/references must not be accessed outside the JS thread");
514
515 std::unique_ptr<T> result;
516 invokeAndWaitForContinue(
517 [&](QOhosJsState &jsState, QOhosTaskPromise<> taskPromise) {
518 evalFunc(
519 jsState,
520 QOhosTaskPromise<T>(
521 [&result, taskPromise = std::move(taskPromise)](T value) {
522 result = std::make_unique<T>(std::move(value));
523 taskPromise();
524 },
525 {},
526 std::move(callerContextName)));
527 },
528 callerContextName);
529
530 return std::move(*result);
531}
532
533QT_END_NAMESPACE
534
535#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 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)
QOhosJsErrorInfo tryExtractErrorInfoFromJsError(QNapi::Value jsErrorOrEmpty)
void invokeInQtThread(std::function< void()> &&task)
static QOhosJsThreadOps * qOhosJsThreadOpsInstance
std::optional< std::string > optName
std::optional< std::string > optMessage
std::optional< std::int64_t > optCode