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 <utility>
36#include <vector>
37
38QT_BEGIN_NAMESPACE
39
40namespace QtOhos {
41
42template<typename Enum>
44{
45};
46
47}
48
49class Q_CORE_EXPORT QOhosJsState
50{
51public:
52 QOhosJsState(const QOhosJsState &) = delete;
53 QOhosJsState &operator=(const QOhosJsState &) = delete;
54
55 virtual ~QOhosJsState();
56
57 virtual napi_env env() = 0;
58
59 virtual QNapi::Object defaultWindowStageOrEmpty() = 0;
60 virtual QNapi::Object defaultUiContextOrEmpty() = 0;
61
62 template<typename T = QNapi::Value>
63 T eval(const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs = {});
64
65 QNapi::Promise evalToPromiseOrRejectOnThrow(
66 const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs = {});
67
68 template<typename Enum>
69 QNapi::Number mapOhosEnumToJs(Enum enumValue);
70
71 template<typename Enum>
72 std::optional<Enum> tryMapOhosEnumFromJs(QNapi::Number enumJsValue);
73
74 template<typename Enum>
75 Enum mapOhosEnumFromJs(QNapi::Number enumJsValue);
76
77protected:
78 struct OhosEnumInfo
79 {
80 std::string fullTypeName;
81 std::vector<std::pair<int, const char *>> enumeratorsNames;
82 };
83
84 QOhosJsState();
85
86private:
87 template<typename Enum, typename = void>
88 struct OhosEnumFullTypeNameFetcher
89 {
90 static std::string fullTypeName()
91 {
92 return QtOhos::OhosEnumMeta<Enum>::moduleName + std::string(".") + QtOhos::OhosEnumMeta<Enum>::typeName;
93 }
94 };
95
96 template<typename Enum>
97 struct OhosEnumFullTypeNameFetcher<Enum, decltype(static_cast<void>(&QtOhos::OhosEnumMeta<Enum>::fullTypeName))>
98 {
99 static std::string fullTypeName()
100 {
101 return QtOhos::OhosEnumMeta<Enum>::fullTypeName;
102 }
103 };
104
105 template<typename Enum>
106 static OhosEnumInfo makeOhosEnumInfo();
107
108 virtual std::tuple<QNapi::Object, std::string> extractModuleFromEvalExpr(const std::string &expr) = 0;
109 virtual QNapi::Number mapOhosEnumToJs(int enumValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) = 0;
110 virtual std::optional<int> tryMapOhosEnumFromJs(QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) = 0;
111 virtual int mapOhosEnumFromJs(QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) = 0;
112};
113
114class Q_CORE_EXPORT QOhosCallbackInfo : public QNapi::CallbackInfo
115{
116public:
117 using QNapi::CallbackInfo::CallbackInfo;
118
119 QOhosJsState &jsState() const;
120};
121
123
124Q_CORE_EXPORT void invoke(std::function<void(QOhosJsState &)> task);
125
126Q_CORE_EXPORT void invokeAndWaitForContinue(
127 std::function<void(QOhosJsState &, std::function<void()>)> &&task);
128
129Q_CORE_EXPORT void runAndWait(const std::function<void(QOhosJsState &)> &task);
130
131template<typename Func>
132auto eval(Func &&func) -> decltype(func(std::declval<QOhosJsState &>()));
133
134template<typename T>
135T evalWithConsumer(std::function<void(QOhosJsState &, std::function<void(T)>)> evalFunc);
136
137}
138
139class Q_CORE_EXPORT QOhosJsThreadOps
140{
141public:
142 virtual ~QOhosJsThreadOps();
143
144 virtual QOhosJsState &jsState() = 0;
145
146 virtual void invoke(std::function<void(QOhosJsState &)> task) = 0;
147 virtual void invokeAndWaitForContinue(
148 std::function<void(QOhosJsState &, std::function<void()>)> &&task) = 0;
149 virtual void runAndWait(const std::function<void(QOhosJsState &)> &task) = 0;
150
151 static void registerInstance(QOhosJsThreadOps *ops);
152 static QOhosJsThreadOps &instance();
153
154protected:
155 QOhosJsThreadOps();
156};
157
158namespace QtOhos {
159
160class Q_CORE_EXPORT QObjectThreadSafeRef
161{
162public:
165
168
169 bool operator==(const QObjectThreadSafeRef &other) const;
170 bool operator!=(const QObjectThreadSafeRef &other) const;
171
172 std::string refName() const;
173
174 // it may be called only inside Qt thread that created the reference
175 QPointer<QObject> data() const;
176
178
179private:
180 struct QObjectRef
181 {
184 };
185
187 Q_CONSTINIT inline static std::uint64_t refsMapInsertCounter = 0;
188
192
193public:
195};
196
197template<typename T>
199{
200public:
201 static_assert(std::is_base_of<QObject, T>::value, "The class supports QObject subtypes only");
202
204 QThreadSafeRef(QPointer<T> obj);
205
208
210
211 std::string refName() const;
212
213 // it may be called only inside Qt thread that created the reference
214 QPointer<T> data() const;
215
216 void visitInQtThreadIfAlive(std::function<void(T &)> visitFunc) const;
217
218private:
219 QObjectThreadSafeRef m_ref;
220};
221
223{
224public:
225 QtState(const QtState &) = delete;
226 QtState &operator=(const QtState &) = delete;
227
228 virtual ~QtState();
229
230 virtual bool isQtThread() const = 0;
231
232 virtual void invokeTask(std::function<void()> &&task) = 0;
233
234protected:
236};
237
238template<typename T>
240
241// this function should be called once from Qt thread at some point during startup
242Q_CORE_EXPORT void initQtThreadState();
243
244// Creates a proxy for the base shared_ptr which uses custom deleter to ensure
245// that the underlying shared_ptr is destroyed (synchronously) in the JS thread.
246// This ensures that the managed object will be also destroyed in the JS thread,
247// as long as the caller passes the only existing shared_ptr instance managing
248// that object to the function. This is the caller's responsibility to do this
249// (preferably by using a call to some std::shared_ptr<T> factory function as
250// the function's argument).
251template<typename T>
252std::shared_ptr<T> makeProxyWithJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr);
253
254// invokes the task inside the Qt thread, can be called from any thread at any time
255Q_CORE_EXPORT void invokeInQtThread(std::function<void()> task);
256
257Q_CORE_EXPORT QtState &getQtState();
258
259// logs error message with supplied prefix and error details extracted from
260// JS error object passed as an argument via CallbackInfo
261Q_CORE_EXPORT void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix);
262
263// Creates JS callback that expects JS error object as a JS argument
264// and logs details of the error. The logged message includes provided
265// call context string (usually the name of called method).
267
268template<typename T>
270{
271 return QThreadSafeRef<T>(obj);
272}
273
274template<typename T>
275QThreadSafeRef<T>::QThreadSafeRef() = default;
276
277template<typename T>
278QThreadSafeRef<T>::QThreadSafeRef(QPointer<T> obj)
279 : QObjectThreadSafeRef(obj.data())
280{
281}
282
283template<typename T>
284QThreadSafeRef<T>::QThreadSafeRef(const QThreadSafeRef<T> &other) = default;
285
286template<typename T>
287QThreadSafeRef<T> &QThreadSafeRef<T>::operator=(const QThreadSafeRef<T> &other) = default;
288
289template<typename T>
290std::string QThreadSafeRef<T>::refName() const
291{
292 return QObjectThreadSafeRef::refName();
293}
294
295template<typename T>
297{
298 return static_cast<const QObjectThreadSafeRef &>(*this);
299}
300
301template<typename T>
303{
304 return static_cast<T *>(
305 static_cast<QObject *>(
306 QObjectThreadSafeRef::data()));
307}
308
309template<typename T>
310void QThreadSafeRef<T>::visitInQtThreadIfAlive(std::function<void(T &)> visitFunc) const
311{
312 QObjectThreadSafeRef::visitInQtThreadIfAlive(
313 [visitFunc = std::move(visitFunc)](QObject &obj) {
314 visitFunc(static_cast<T &>(obj));
315 });
316}
317
318template<typename T>
319std::shared_ptr<T> makeProxyWithJsThreadDeleter(std::shared_ptr<T> &&baseSharedPtr)
320{
321 auto *baseRawPtr = baseSharedPtr.get();
322 return std::shared_ptr<T>(
323 baseRawPtr,
324 [baseSharedPtr = std::move(baseSharedPtr)](T *) mutable {
326 [&](QOhosJsState &) {
327 baseSharedPtr.reset();
328 });
329 });
330}
331
332}
333
334template<typename T>
335T QOhosJsState::eval(const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs)
336{
337 using namespace std::string_literals;
338
339 QNapi::Object module;
340 std::string subExpr;
341 std::tie(module, subExpr) = extractModuleFromEvalExpr(expr);
342
343 return !subExpr.empty()
344 ? module.eval<T>(subExpr, exprArgs)
345 : QNapi::checkedCast<T>(
346 module,
347 [&]() {
348 return "module '"s + expr + "'"s;
349 });
350}
351
352inline QNapi::Promise QOhosJsState::evalToPromiseOrRejectOnThrow(
353 const std::string &expr, const std::vector<QNapi::ValueWrapper> &exprArgs)
354{
355 QNapi::Object module;
356 std::string subExpr;
357 std::tie(module, subExpr) = extractModuleFromEvalExpr(expr);
358
359 return module.evalToPromiseOrRejectOnThrow(subExpr, exprArgs);
360}
361
362template<typename Enum>
363QNapi::Number QOhosJsState::mapOhosEnumToJs(Enum enumValue)
364{
365 return mapOhosEnumToJs(static_cast<int>(enumValue), typeid(Enum), &makeOhosEnumInfo<Enum>);
366}
367
368template<typename Enum>
369std::optional<Enum> QOhosJsState::tryMapOhosEnumFromJs(QNapi::Number enumJsValue)
370{
371 auto intValue = tryMapOhosEnumFromJs(enumJsValue, typeid(Enum), &makeOhosEnumInfo<Enum>);
372 return intValue.has_value()
373 ? std::optional(static_cast<Enum>(intValue.value()))
374 : std::nullopt;
375}
376
377template<typename Enum>
378Enum QOhosJsState::mapOhosEnumFromJs(QNapi::Number enumJsValue)
379{
380 return static_cast<Enum>(mapOhosEnumFromJs(enumJsValue, typeid(Enum), &makeOhosEnumInfo<Enum>));
381}
382
383template<typename Enum>
384QOhosJsState::OhosEnumInfo QOhosJsState::makeOhosEnumInfo()
385{
386 static const auto enumEnumeratorsNames = QtOhos::OhosEnumMeta<Enum>::enumeratorsNames;
387
388 std::vector<std::pair<int, const char *>> intEnumeratorsNames;
389 std::transform(
390 enumEnumeratorsNames.begin(), enumEnumeratorsNames.end(),
391 std::back_inserter(intEnumeratorsNames),
392 [](const auto &valueNamePair) {
393 return std::pair<int, const char *>(static_cast<int>(valueNamePair.first), valueNamePair.second);
394 });
395
396 return OhosEnumInfo {
397 .fullTypeName = OhosEnumFullTypeNameFetcher<Enum>::fullTypeName(),
398 .enumeratorsNames = std::move(intEnumeratorsNames),
399 };
400}
401
402template<typename Func>
403auto QOhosJsThreadGateway::eval(Func &&func) -> decltype(func(std::declval<QOhosJsState &>()))
404{
405 using Result = decltype(func(std::declval<QOhosJsState &>()));
406 static_assert(
407 !(std::is_class<Result>::value
408 && (
409 std::is_convertible<Result, napi_value>::value
410 || std::is_convertible<Result, napi_ref>::value)),
411 "NAPI values/references must not be accessed outside the JS thread");
412
413 std::unique_ptr<Result> result;
414 runAndWait(
415 [&](QOhosJsState &jsState) {
416 result = std::make_unique<Result>(func(jsState));
417 });
418 return std::move(*result);
419}
420
421template<typename T>
422T QOhosJsThreadGateway::evalWithConsumer(std::function<void(QOhosJsState &, std::function<void(T)>)> evalFunc)
423{
424 static_assert(
425 !(std::is_class<T>::value
426 && (
427 std::is_convertible<T, napi_value>::value
428 || std::is_convertible<T, napi_ref>::value)),
429 "NAPI values/references must not be accessed outside the JS thread");
430
431 std::unique_ptr<T> result;
432 invokeAndWaitForContinue(
433 [&](QOhosJsState &jsState, std::function<void()> continueFunc) {
434 evalFunc(
435 jsState,
436 [&result, continueFunc = std::move(continueFunc)](T value) {
437 result = std::make_unique<T>(std::move(value));
438 continueFunc();
439 });
440 });
441
442 return std::move(*result);
443}
444
445QT_END_NAMESPACE
446
447#endif
QPointer< T > data() const
QThreadSafeRef(const QThreadSafeRef< T > &other)
void visitInQtThreadIfAlive(std::function< void(T &)> visitFunc) const
std::string refName() 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()
Q_CORE_EXPORT void invokeAndWaitForContinue(std::function< void(QOhosJsState &, std::function< void()>)> &&task)
Q_CORE_EXPORT void invoke(std::function< void(QOhosJsState &)> task)
T evalWithConsumer(std::function< void(QOhosJsState &, std::function< void(T)>)> evalFunc)
Q_CORE_EXPORT void runAndWait(const std::function< void(QOhosJsState &)> &task)
auto eval(Func &&func) -> decltype(func(std::declval< QOhosJsState & >()))
void invokeInQtThread(std::function< void()> task)
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::function< void(const QOhosCallbackInfo &)> makeErrorLoggingJsCallback(std::string callContext)
static QOhosJsThreadOps * qOhosJsThreadOpsInstance