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