react-native-nitro-modules 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroModules.podspec +4 -1
- package/README.md +35 -16
- package/android/gradle.properties +5 -5
- package/android/src/main/cpp/JNIOnLoad.cpp +8 -0
- package/android/src/main/cpp/core/ByteBufferArrayBuffer.hpp +47 -0
- package/android/src/main/cpp/core/JAnyMap.hpp +193 -0
- package/android/src/main/cpp/core/JAnyValue.hpp +191 -0
- package/android/src/main/cpp/core/JArrayBuffer.hpp +119 -0
- package/android/src/main/cpp/core/JHybridObject.hpp +11 -4
- package/android/src/main/cpp/core/JPromise.hpp +88 -0
- package/android/src/main/cpp/platform/NitroLogger.cpp +36 -0
- package/android/src/main/cpp/registry/JHybridObjectInitializer.hpp +1 -1
- package/android/src/main/cpp/registry/JHybridObjectRegistry.cpp +1 -1
- package/android/src/main/cpp/registry/JHybridObjectRegistry.hpp +1 -1
- package/android/src/main/cpp/utils/{JNISharedPtr.h → JNISharedPtr.hpp} +13 -1
- package/android/src/main/java/com/margelo/nitro/JNIOnLoad.java +27 -0
- package/android/src/main/java/com/margelo/nitro/NitroModulesPackage.java +1 -9
- package/android/src/main/java/com/margelo/nitro/core/AnyMap.kt +61 -0
- package/android/src/main/java/com/margelo/nitro/core/AnyValue.kt +153 -0
- package/android/src/main/java/com/margelo/nitro/core/ArrayBuffer.kt +84 -0
- package/android/src/main/java/com/margelo/nitro/{HybridObject.kt → core/HybridObject.kt} +12 -15
- package/android/src/main/java/com/margelo/nitro/{HybridObjectInitializer.java → core/HybridObjectInitializer.java} +1 -1
- package/android/src/main/java/com/margelo/nitro/{HybridObjectRegistry.java → core/HybridObjectRegistry.java} +6 -4
- package/android/src/main/java/com/margelo/nitro/core/Promise.kt +115 -0
- package/cpp/core/AnyMap.hpp +2 -2
- package/cpp/core/ArrayBuffer.cpp +5 -5
- package/cpp/core/ArrayBuffer.hpp +12 -7
- package/cpp/core/HybridFunction.hpp +119 -45
- package/cpp/core/HybridObject.cpp +17 -6
- package/cpp/core/HybridObject.hpp +32 -8
- package/cpp/jsi/JSICache.cpp +5 -5
- package/cpp/jsi/JSICache.hpp +1 -3
- package/cpp/jsi/JSIConverter+AnyMap.hpp +2 -2
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +2 -2
- package/cpp/jsi/JSIConverter+Function.hpp +7 -6
- package/cpp/jsi/JSIConverter+HybridObject.hpp +6 -5
- package/cpp/jsi/JSIConverter+Optional.hpp +1 -1
- package/cpp/jsi/JSIConverter+Promise.hpp +6 -4
- package/cpp/jsi/JSIConverter+Tuple.hpp +1 -1
- package/cpp/jsi/JSIConverter+UnorderedMap.hpp +1 -1
- package/cpp/jsi/JSIConverter+Variant.hpp +1 -3
- package/cpp/jsi/JSIConverter+Vector.hpp +1 -1
- package/cpp/jsi/JSIConverter.hpp +11 -11
- package/cpp/jsi/JSPromise.cpp +2 -2
- package/cpp/platform/NitroLogger.hpp +67 -0
- package/cpp/prototype/HybridObjectPrototype.cpp +2 -2
- package/cpp/prototype/Prototype.hpp +43 -22
- package/cpp/registry/HybridObjectRegistry.cpp +4 -4
- package/cpp/threading/Dispatcher.cpp +4 -3
- package/cpp/threading/ThreadPool.cpp +2 -2
- package/cpp/turbomodule/NativeNitroModules.cpp +26 -8
- package/cpp/turbomodule/NativeNitroModules.h +1 -0
- package/cpp/turbomodule/NativeNitroModules.hpp +2 -0
- package/cpp/utils/TypeInfo.hpp +14 -0
- package/ios/core/AnyMapHolder.hpp +91 -0
- package/ios/core/AnyMapHolder.swift +316 -0
- package/ios/core/ArrayBufferHolder.hpp +16 -5
- package/ios/core/{ArrayBuffer.swift → ArrayBufferHolder.swift} +15 -17
- package/ios/core/HybridContext.hpp +3 -3
- package/ios/core/Promise.swift +182 -0
- package/ios/core/PromiseHolder.hpp +86 -0
- package/ios/platform/NitroLogger.mm +36 -0
- package/ios/platform/ThreadUtils.cpp +1 -1
- package/ios/turbomodule/NitroModuleOnLoad.mm +6 -0
- package/ios/utils/SwiftClosure.hpp +63 -0
- package/ios/utils/SwiftClosure.swift +58 -0
- package/lib/AnyMap.d.ts +3 -0
- package/lib/HybridObject.d.ts +15 -0
- package/lib/NativeNitroModules.d.ts +2 -0
- package/lib/NitroModules.d.ts +18 -0
- package/lib/NitroModules.js +24 -0
- package/lib/commonjs/NativeNitroModules.js.map +1 -1
- package/lib/commonjs/NitroModules.js +24 -0
- package/lib/commonjs/NitroModules.js.map +1 -1
- package/lib/commonjs/package.json +1 -0
- package/lib/module/AnyMap.js +1 -1
- package/lib/module/HybridObject.js +1 -1
- package/lib/module/ModuleNotFoundError.js +2 -0
- package/lib/module/ModuleNotFoundError.js.map +1 -1
- package/lib/module/NativeNitroModules.js +2 -0
- package/lib/module/NativeNitroModules.js.map +1 -1
- package/lib/module/NativeNitroModules.web.js +2 -0
- package/lib/module/NativeNitroModules.web.js.map +1 -1
- package/lib/module/NitroModules.js +26 -0
- package/lib/module/NitroModules.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/typescript/AnyMap.d.ts +20 -0
- package/lib/typescript/AnyMap.d.ts.map +1 -1
- package/lib/typescript/HybridObject.d.ts +98 -0
- package/lib/typescript/HybridObject.d.ts.map +1 -1
- package/lib/typescript/NativeNitroModules.d.ts +2 -0
- package/lib/typescript/NativeNitroModules.d.ts.map +1 -1
- package/lib/typescript/NitroModules.d.ts +18 -0
- package/lib/typescript/NitroModules.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/AnyMap.ts +3 -0
- package/src/HybridObject.ts +15 -0
- package/src/NativeNitroModules.ts +5 -0
- package/src/NitroModules.ts +24 -0
- package/android/src/main/cpp/core/JHybridObject.cpp +0 -8
- package/cpp/templates/IsInPack.hpp +0 -21
- package/cpp/utils/NitroLogger.hpp +0 -58
- package/ios/core/ClosureWrapper.swift +0 -25
- package/ios/core/HybridContext.cpp +0 -8
- package/ios/core/Promise.cpp +0 -10
- package/ios/core/Promise.hpp +0 -43
package/cpp/jsi/JSICache.cpp
CHANGED
|
@@ -24,7 +24,7 @@ inline void destroyReferences(const std::vector<BorrowingReference<T>>& referenc
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
JSICache::~JSICache() {
|
|
27
|
-
Logger::log(TAG, "Destroying JSICache...");
|
|
27
|
+
Logger::log(LogLevel::Info, TAG, "Destroying JSICache...");
|
|
28
28
|
std::unique_lock lock(_mutex);
|
|
29
29
|
|
|
30
30
|
destroyReferences(_objectCache);
|
|
@@ -43,19 +43,19 @@ JSICacheReference JSICache::getOrCreateCache(jsi::Runtime& runtime) {
|
|
|
43
43
|
// It's still alive! Return it
|
|
44
44
|
return JSICacheReference(strong);
|
|
45
45
|
}
|
|
46
|
-
Logger::log(TAG, "JSICache was created, but it is no longer strong!");
|
|
46
|
+
Logger::log(LogLevel::Warning, TAG, "JSICache was created, but it is no longer strong!");
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
#
|
|
49
|
+
#ifndef NDEBUG
|
|
50
50
|
if (runtime.global().hasProperty(runtime, CACHE_PROP_NAME)) [[unlikely]] {
|
|
51
51
|
throw std::runtime_error("The Runtime \"" + getRuntimeId(runtime) + "\" already has a global cache! (\"" + CACHE_PROP_NAME + "\")");
|
|
52
52
|
}
|
|
53
53
|
#endif
|
|
54
54
|
|
|
55
55
|
// Cache doesn't exist yet.
|
|
56
|
-
Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
|
|
56
|
+
Logger::log(LogLevel::Info, TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime).c_str());
|
|
57
57
|
// Create new cache
|
|
58
|
-
auto nativeState = std::shared_ptr<JSICache>(new JSICache(
|
|
58
|
+
auto nativeState = std::shared_ptr<JSICache>(new JSICache());
|
|
59
59
|
// Wrap it in a jsi::Value using NativeState
|
|
60
60
|
jsi::Object cache(runtime);
|
|
61
61
|
cache.setNativeState(runtime, nativeState);
|
package/cpp/jsi/JSICache.hpp
CHANGED
|
@@ -38,12 +38,11 @@ public:
|
|
|
38
38
|
~JSICache();
|
|
39
39
|
|
|
40
40
|
public:
|
|
41
|
-
JSICache() = delete;
|
|
42
41
|
JSICache(const JSICache&) = delete;
|
|
43
42
|
JSICache(JSICache&&) = delete;
|
|
44
43
|
|
|
45
44
|
private:
|
|
46
|
-
|
|
45
|
+
JSICache() = default;
|
|
47
46
|
|
|
48
47
|
public:
|
|
49
48
|
/**
|
|
@@ -59,7 +58,6 @@ private:
|
|
|
59
58
|
friend class JSICacheReference;
|
|
60
59
|
|
|
61
60
|
private:
|
|
62
|
-
jsi::Runtime* _runtime;
|
|
63
61
|
std::mutex _mutex;
|
|
64
62
|
std::vector<BorrowingReference<jsi::Object>> _objectCache;
|
|
65
63
|
std::vector<BorrowingReference<jsi::Function>> _functionCache;
|
|
@@ -27,7 +27,7 @@ using namespace facebook;
|
|
|
27
27
|
|
|
28
28
|
// AnyValue <> Record<K, V>
|
|
29
29
|
template <>
|
|
30
|
-
struct JSIConverter<AnyValue> {
|
|
30
|
+
struct JSIConverter<AnyValue> final {
|
|
31
31
|
static inline AnyValue fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
32
32
|
return JSIConverter<AnyValue::variant>::fromJSI(runtime, arg);
|
|
33
33
|
}
|
|
@@ -41,7 +41,7 @@ struct JSIConverter<AnyValue> {
|
|
|
41
41
|
|
|
42
42
|
// AnyMap <> Record<K, V>
|
|
43
43
|
template <>
|
|
44
|
-
struct JSIConverter<std::shared_ptr<AnyMap>> {
|
|
44
|
+
struct JSIConverter<std::shared_ptr<AnyMap>> final {
|
|
45
45
|
static inline std::shared_ptr<AnyMap> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
46
46
|
jsi::Object object = arg.asObject(runtime);
|
|
47
47
|
jsi::Array propNames = object.getPropertyNames(runtime);
|
|
@@ -28,11 +28,11 @@ using namespace facebook;
|
|
|
28
28
|
|
|
29
29
|
// MutableBuffer <> ArrayBuffer
|
|
30
30
|
template <typename T>
|
|
31
|
-
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer>>> {
|
|
31
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer>>> final {
|
|
32
32
|
static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
33
33
|
jsi::Object object = arg.asObject(runtime);
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
#ifndef NDEBUG
|
|
36
36
|
if (!object.isArrayBuffer(runtime)) [[unlikely]] {
|
|
37
37
|
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) +
|
|
38
38
|
"\" is not an ArrayBuffer! "
|
|
@@ -27,7 +27,7 @@ using namespace facebook;
|
|
|
27
27
|
|
|
28
28
|
// [](Args...) -> T {} <> (Args...) => T
|
|
29
29
|
template <typename ReturnType, typename... Args>
|
|
30
|
-
struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
30
|
+
struct JSIConverter<std::function<ReturnType(Args...)>> final {
|
|
31
31
|
// std::future<T> -> T
|
|
32
32
|
using ResultingType = future_type_v<ReturnType>;
|
|
33
33
|
|
|
@@ -47,7 +47,8 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
47
47
|
std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
|
|
48
48
|
if (!dispatcher) {
|
|
49
49
|
if constexpr (std::is_void_v<ResultingType>) {
|
|
50
|
-
Logger::log("JSIConverter",
|
|
50
|
+
Logger::log(LogLevel::Error, "JSIConverter",
|
|
51
|
+
"Tried calling void(..) function, but the JS Dispatcher has already been deleted by JS!");
|
|
51
52
|
return;
|
|
52
53
|
} else {
|
|
53
54
|
throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
|
|
@@ -67,9 +68,9 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::function<ReturnType(Args...)
|
|
71
|
-
jsi::HostFunctionType jsFunction = [function
|
|
72
|
-
|
|
71
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::function<ReturnType(Args...)>& function) {
|
|
72
|
+
jsi::HostFunctionType jsFunction = [function](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* args,
|
|
73
|
+
size_t count) -> jsi::Value {
|
|
73
74
|
if (count != sizeof...(Args)) [[unlikely]] {
|
|
74
75
|
throw jsi::JSError(runtime, "Function expected " + std::to_string(sizeof...(Args)) + " arguments, but received " +
|
|
75
76
|
std::to_string(count) + "!");
|
|
@@ -95,7 +96,7 @@ private:
|
|
|
95
96
|
if (!function) {
|
|
96
97
|
if constexpr (std::is_void_v<ResultingType>) {
|
|
97
98
|
// runtime has already been deleted. since this returns void, we can just ignore it being deleted.
|
|
98
|
-
Logger::log("JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
|
|
99
|
+
Logger::log(LogLevel::Error, "JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
|
|
99
100
|
return;
|
|
100
101
|
} else {
|
|
101
102
|
// runtime has already been deleted, but we are expecting a return value - throw an error in this case.
|
|
@@ -20,11 +20,11 @@ using namespace facebook;
|
|
|
20
20
|
|
|
21
21
|
// HybridObject(NativeState) <> {}
|
|
22
22
|
template <typename T>
|
|
23
|
-
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> {
|
|
23
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> final {
|
|
24
24
|
using TPointee = typename T::element_type;
|
|
25
25
|
|
|
26
26
|
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
27
|
-
#
|
|
27
|
+
#ifndef NDEBUG
|
|
28
28
|
if (!arg.isObject()) [[unlikely]] {
|
|
29
29
|
if (arg.isUndefined()) [[unlikely]] {
|
|
30
30
|
throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
|
|
@@ -36,7 +36,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
|
|
|
36
36
|
#endif
|
|
37
37
|
jsi::Object object = arg.asObject(runtime);
|
|
38
38
|
|
|
39
|
-
#
|
|
39
|
+
#ifndef NDEBUG
|
|
40
40
|
if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
|
|
41
41
|
if (!object.hasNativeState(runtime)) [[unlikely]] {
|
|
42
42
|
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
@@ -47,7 +47,8 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
#endif
|
|
50
|
-
|
|
50
|
+
std::shared_ptr<jsi::NativeState> nativeState = object.getNativeState(runtime);
|
|
51
|
+
return std::dynamic_pointer_cast<TPointee>(nativeState);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
|
|
@@ -60,7 +61,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
|
|
|
60
61
|
// It's a HybridObject - use it's internal constructor which caches jsi::Objects for proper memory management!
|
|
61
62
|
return arg->toObject(runtime);
|
|
62
63
|
} else {
|
|
63
|
-
// It's any other kind of jsi::
|
|
64
|
+
// It's any other kind of jsi::NativeState - just create it as normal. This will not have a prototype then!
|
|
64
65
|
jsi::Object object(runtime);
|
|
65
66
|
object.setNativeState(runtime, arg);
|
|
66
67
|
return object;
|
|
@@ -21,7 +21,7 @@ using namespace facebook;
|
|
|
21
21
|
|
|
22
22
|
// std::optional<T> <> T | undefined
|
|
23
23
|
template <typename TInner>
|
|
24
|
-
struct JSIConverter<std::optional<TInner>> {
|
|
24
|
+
struct JSIConverter<std::optional<TInner>> final {
|
|
25
25
|
static inline std::optional<TInner> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
26
26
|
if (arg.isUndefined() || arg.isNull()) {
|
|
27
27
|
return std::nullopt;
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
9
|
class Dispatcher;
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
class JSPromise;
|
|
11
12
|
|
|
12
13
|
template <typename T, typename Enable>
|
|
13
14
|
struct JSIConverter;
|
|
@@ -29,7 +30,7 @@ using namespace facebook;
|
|
|
29
30
|
|
|
30
31
|
// std::future<T> <> Promise<T>
|
|
31
32
|
template <typename TResult>
|
|
32
|
-
struct JSIConverter<std::future<TResult>> {
|
|
33
|
+
struct JSIConverter<std::future<TResult>> final {
|
|
33
34
|
static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
|
|
34
35
|
throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
|
|
35
36
|
}
|
|
@@ -48,7 +49,8 @@ struct JSIConverter<std::future<TResult>> {
|
|
|
48
49
|
// the async function completed successfully, get a JS Dispatcher so we can resolve on JS Thread
|
|
49
50
|
std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
|
|
50
51
|
if (!dispatcher) {
|
|
51
|
-
Logger::log("JSIConverter",
|
|
52
|
+
Logger::log(LogLevel::Error, "JSIConverter",
|
|
53
|
+
"Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed!");
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -83,7 +85,7 @@ struct JSIConverter<std::future<TResult>> {
|
|
|
83
85
|
});
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
static inline bool canConvert(jsi::Runtime
|
|
88
|
+
static inline bool canConvert(jsi::Runtime&, const jsi::Value&) {
|
|
87
89
|
throw std::runtime_error("jsi::Value of type Promise cannot be converted to std::future yet!");
|
|
88
90
|
}
|
|
89
91
|
};
|
|
@@ -23,7 +23,7 @@ using namespace facebook;
|
|
|
23
23
|
|
|
24
24
|
// std::tuple<A, B, C> <> [A, B, C]
|
|
25
25
|
template <typename... Types>
|
|
26
|
-
struct JSIConverter<std::tuple<Types...>> {
|
|
26
|
+
struct JSIConverter<std::tuple<Types...>> final {
|
|
27
27
|
static inline std::tuple<Types...> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
28
28
|
jsi::Object object = value.asObject(runtime);
|
|
29
29
|
jsi::Array array = object.asArray(runtime);
|
|
@@ -23,7 +23,7 @@ using namespace facebook;
|
|
|
23
23
|
|
|
24
24
|
// std::unordered_map<std::string, T> <> Record<string, T>
|
|
25
25
|
template <typename ValueType>
|
|
26
|
-
struct JSIConverter<std::unordered_map<std::string, ValueType>> {
|
|
26
|
+
struct JSIConverter<std::unordered_map<std::string, ValueType>> final {
|
|
27
27
|
static inline std::unordered_map<std::string, ValueType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
28
28
|
jsi::Object object = arg.asObject(runtime);
|
|
29
29
|
jsi::Array propertyNames = object.getPropertyNames(runtime);
|
|
@@ -18,7 +18,6 @@ struct JSIConverter;
|
|
|
18
18
|
#include "JSIConverter.hpp"
|
|
19
19
|
|
|
20
20
|
#include "AnyMap.hpp"
|
|
21
|
-
#include "IsInPack.hpp"
|
|
22
21
|
#include "TypeInfo.hpp"
|
|
23
22
|
#include <jsi/jsi.h>
|
|
24
23
|
#include <memory>
|
|
@@ -30,8 +29,7 @@ using namespace facebook;
|
|
|
30
29
|
|
|
31
30
|
// std::variant<A, B, C> <> A | B | C
|
|
32
31
|
template <typename... Types>
|
|
33
|
-
struct JSIConverter<std::variant<Types...>> {
|
|
34
|
-
|
|
32
|
+
struct JSIConverter<std::variant<Types...>> final {
|
|
35
33
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
36
34
|
// Check each type in `Types...` to make sure we can convert `jsi::Value` to one of those.
|
|
37
35
|
return (JSIConverter<Types>::canConvert(runtime, value) || ...);
|
|
@@ -22,7 +22,7 @@ using namespace facebook;
|
|
|
22
22
|
|
|
23
23
|
// std::vector<T> <> T[]
|
|
24
24
|
template <typename ElementType>
|
|
25
|
-
struct JSIConverter<std::vector<ElementType>> {
|
|
25
|
+
struct JSIConverter<std::vector<ElementType>> final {
|
|
26
26
|
static inline std::vector<ElementType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
27
27
|
jsi::Array array = arg.asObject(runtime).asArray(runtime);
|
|
28
28
|
size_t length = array.size(runtime);
|
package/cpp/jsi/JSIConverter.hpp
CHANGED
|
@@ -22,7 +22,7 @@ using namespace facebook;
|
|
|
22
22
|
* The JSIConverter<T> class can convert any type from and to a jsi::Value.
|
|
23
23
|
* It uses templates to statically create fromJSI/toJSI methods, and will throw compile-time errors
|
|
24
24
|
* if a given type is not convertable.
|
|
25
|
-
* Value types, custom types (
|
|
25
|
+
* Value types, custom types (HybridObject), and even functions with any number of arguments/types are supported.
|
|
26
26
|
* This type can be extended by just creating a new template for JSIConverter in a header.
|
|
27
27
|
*/
|
|
28
28
|
template <typename T, typename Enable = void>
|
|
@@ -61,7 +61,7 @@ private:
|
|
|
61
61
|
|
|
62
62
|
// int <> number
|
|
63
63
|
template <>
|
|
64
|
-
struct JSIConverter<int> {
|
|
64
|
+
struct JSIConverter<int> final {
|
|
65
65
|
static inline int fromJSI(jsi::Runtime&, const jsi::Value& arg) {
|
|
66
66
|
return static_cast<int>(arg.asNumber());
|
|
67
67
|
}
|
|
@@ -75,11 +75,11 @@ struct JSIConverter<int> {
|
|
|
75
75
|
|
|
76
76
|
// std::monostate <> null
|
|
77
77
|
template <>
|
|
78
|
-
struct JSIConverter<std::monostate> {
|
|
79
|
-
static inline std::monostate fromJSI(jsi::Runtime&, const jsi::Value&
|
|
78
|
+
struct JSIConverter<std::monostate> final {
|
|
79
|
+
static inline std::monostate fromJSI(jsi::Runtime&, const jsi::Value&) {
|
|
80
80
|
return std::monostate();
|
|
81
81
|
}
|
|
82
|
-
static inline jsi::Value toJSI(jsi::Runtime&, std::monostate
|
|
82
|
+
static inline jsi::Value toJSI(jsi::Runtime&, std::monostate) {
|
|
83
83
|
return jsi::Value::null();
|
|
84
84
|
}
|
|
85
85
|
static inline bool canConvert(jsi::Runtime&, const jsi::Value& value) {
|
|
@@ -89,7 +89,7 @@ struct JSIConverter<std::monostate> {
|
|
|
89
89
|
|
|
90
90
|
// double <> number
|
|
91
91
|
template <>
|
|
92
|
-
struct JSIConverter<double> {
|
|
92
|
+
struct JSIConverter<double> final {
|
|
93
93
|
static inline double fromJSI(jsi::Runtime&, const jsi::Value& arg) {
|
|
94
94
|
return arg.asNumber();
|
|
95
95
|
}
|
|
@@ -103,7 +103,7 @@ struct JSIConverter<double> {
|
|
|
103
103
|
|
|
104
104
|
// float <> number
|
|
105
105
|
template <>
|
|
106
|
-
struct JSIConverter<float> {
|
|
106
|
+
struct JSIConverter<float> final {
|
|
107
107
|
static inline float fromJSI(jsi::Runtime&, const jsi::Value& arg) {
|
|
108
108
|
return static_cast<float>(arg.asNumber());
|
|
109
109
|
}
|
|
@@ -117,7 +117,7 @@ struct JSIConverter<float> {
|
|
|
117
117
|
|
|
118
118
|
// int64_t <> BigInt
|
|
119
119
|
template <>
|
|
120
|
-
struct JSIConverter<int64_t> {
|
|
120
|
+
struct JSIConverter<int64_t> final {
|
|
121
121
|
static inline double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
122
122
|
return arg.asBigInt(runtime).asInt64(runtime);
|
|
123
123
|
}
|
|
@@ -135,7 +135,7 @@ struct JSIConverter<int64_t> {
|
|
|
135
135
|
|
|
136
136
|
// uint64_t <> BigInt
|
|
137
137
|
template <>
|
|
138
|
-
struct JSIConverter<uint64_t> {
|
|
138
|
+
struct JSIConverter<uint64_t> final {
|
|
139
139
|
static inline double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
140
140
|
return arg.asBigInt(runtime).asUint64(runtime);
|
|
141
141
|
}
|
|
@@ -153,7 +153,7 @@ struct JSIConverter<uint64_t> {
|
|
|
153
153
|
|
|
154
154
|
// bool <> boolean
|
|
155
155
|
template <>
|
|
156
|
-
struct JSIConverter<bool> {
|
|
156
|
+
struct JSIConverter<bool> final {
|
|
157
157
|
static inline bool fromJSI(jsi::Runtime&, const jsi::Value& arg) {
|
|
158
158
|
return arg.asBool();
|
|
159
159
|
}
|
|
@@ -167,7 +167,7 @@ struct JSIConverter<bool> {
|
|
|
167
167
|
|
|
168
168
|
// std::string <> string
|
|
169
169
|
template <>
|
|
170
|
-
struct JSIConverter<std::string> {
|
|
170
|
+
struct JSIConverter<std::string> final {
|
|
171
171
|
static inline std::string fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
172
172
|
return arg.asString(runtime).utf8(runtime);
|
|
173
173
|
}
|
package/cpp/jsi/JSPromise.cpp
CHANGED
|
@@ -38,7 +38,7 @@ void JSPromise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
|
|
|
38
38
|
OwningLock<jsi::Function> lock = _resolver.lock();
|
|
39
39
|
|
|
40
40
|
if (!_resolver) {
|
|
41
|
-
Logger::log(TAG, "Promise resolver function has already been deleted! Ignoring call..");
|
|
41
|
+
Logger::log(LogLevel::Error, TAG, "Promise resolver function has already been deleted! Ignoring call..");
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
_resolver->call(runtime, std::move(result));
|
|
@@ -48,7 +48,7 @@ void JSPromise::reject(jsi::Runtime& runtime, std::string message) {
|
|
|
48
48
|
OwningLock<jsi::Function> lock = _rejecter.lock();
|
|
49
49
|
|
|
50
50
|
if (!_rejecter) {
|
|
51
|
-
Logger::log(TAG, "Promise rejecter function has already been deleted! Ignoring call..");
|
|
51
|
+
Logger::log(LogLevel::Error, TAG, "Promise rejecter function has already been deleted! Ignoring call..");
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
jsi::JSError error(runtime, message);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 05.03.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <cstdarg>
|
|
8
|
+
#include <cstdio>
|
|
9
|
+
#include <iostream>
|
|
10
|
+
#include <sstream>
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <type_traits>
|
|
13
|
+
|
|
14
|
+
namespace margelo::nitro {
|
|
15
|
+
|
|
16
|
+
enum class LogLevel { Debug, Info, Warning, Error };
|
|
17
|
+
|
|
18
|
+
class Logger final {
|
|
19
|
+
private:
|
|
20
|
+
Logger() = delete;
|
|
21
|
+
|
|
22
|
+
public:
|
|
23
|
+
template <typename... Args>
|
|
24
|
+
static void log(LogLevel level, const char* tag, const char* format, Args... args) {
|
|
25
|
+
#ifndef NDEBUG
|
|
26
|
+
// 1. Make sure args can be passed to sprintf(..)
|
|
27
|
+
static_assert(all_are_trivially_copyable<Args...>(), "All arguments passed to Logger::log(..) must be trivially copyable! "
|
|
28
|
+
"Did you try to pass a complex type, like std::string?");
|
|
29
|
+
|
|
30
|
+
// 2. Format all arguments in the message
|
|
31
|
+
std::string message = formatString(format, args...);
|
|
32
|
+
|
|
33
|
+
// 3. Call the platform specific log function
|
|
34
|
+
nativeLog(level, tag, message);
|
|
35
|
+
#endif
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static void nativeLog(LogLevel level, const char* tag, const std::string& string);
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
template <typename... Args>
|
|
42
|
+
static std::string formatString(const char* format, Args... args) {
|
|
43
|
+
#pragma clang diagnostic push
|
|
44
|
+
#pragma clang diagnostic ignored "-Wformat-security"
|
|
45
|
+
int size = snprintf(nullptr, 0, format, args...) + 1; // Extra space for '\0'
|
|
46
|
+
if (size <= 0) {
|
|
47
|
+
return "Error during formatting.";
|
|
48
|
+
}
|
|
49
|
+
std::unique_ptr<char[]> buf(new char[size]);
|
|
50
|
+
snprintf(buf.get(), size, format, args...);
|
|
51
|
+
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
|
52
|
+
#pragma clang diagnostic pop
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Overloaded functions to convert std::string to C-style string
|
|
56
|
+
template <typename T>
|
|
57
|
+
static constexpr bool is_trivially_copyable() {
|
|
58
|
+
return std::is_trivially_copyable<T>::value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
template <typename... Args>
|
|
62
|
+
static constexpr bool all_are_trivially_copyable() {
|
|
63
|
+
return (is_trivially_copyable<Args>() && ...);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
} // namespace margelo::nitro
|
|
@@ -30,7 +30,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
30
30
|
|
|
31
31
|
// 2. We didn't find the given prototype in cache (either it's a new prototype, or a new runtime),
|
|
32
32
|
// so we need to create it. First, we need some helper methods from JS
|
|
33
|
-
Logger::log(TAG, "Creating new JS prototype for C++ instance type \"%s\"...", prototype->getNativeInstanceId().name());
|
|
33
|
+
Logger::log(LogLevel::Info, TAG, "Creating new JS prototype for C++ instance type \"%s\"...", prototype->getNativeInstanceId().name());
|
|
34
34
|
jsi::Object objectConstructor = runtime.global().getPropertyAsObject(runtime, "Object");
|
|
35
35
|
jsi::Function objectCreate = objectConstructor.getPropertyAsFunction(runtime, "create");
|
|
36
36
|
jsi::Function objectDefineProperty = objectConstructor.getPropertyAsFunction(runtime, "defineProperty");
|
|
@@ -69,7 +69,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
69
69
|
prototypeCache.emplace(prototype->getNativeInstanceId(), cachedObject);
|
|
70
70
|
|
|
71
71
|
// 7. In DEBUG, add a __type info to the prototype object.
|
|
72
|
-
#
|
|
72
|
+
#ifndef NDEBUG
|
|
73
73
|
auto typeName = "Prototype<" + std::string(prototype->getNativeInstanceId().name()) + ">";
|
|
74
74
|
cachedObject->setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, typeName));
|
|
75
75
|
#endif
|
|
@@ -38,8 +38,8 @@ using NativeInstanceId = std::type_index;
|
|
|
38
38
|
*/
|
|
39
39
|
struct Prototype final {
|
|
40
40
|
private:
|
|
41
|
-
std::shared_ptr<Prototype> _base = nullptr;
|
|
42
41
|
NativeInstanceId _instanceTypeId;
|
|
42
|
+
std::shared_ptr<Prototype> _base = nullptr;
|
|
43
43
|
std::unordered_map<std::string, HybridFunction> _methods;
|
|
44
44
|
std::unordered_map<std::string, HybridFunction> _getters;
|
|
45
45
|
std::unordered_map<std::string, HybridFunction> _setters;
|
|
@@ -102,25 +102,6 @@ public:
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
public:
|
|
105
|
-
/**
|
|
106
|
-
* Registers the given C++ method as a Hybrid Method that can be called from JS, through the object's Prototype.
|
|
107
|
-
* Example:
|
|
108
|
-
* ```cpp
|
|
109
|
-
* registerHybridMethod("sayHello", &MyObject::sayHello);
|
|
110
|
-
* ```
|
|
111
|
-
*/
|
|
112
|
-
template <typename Derived, typename ReturnType, typename... Args>
|
|
113
|
-
inline void registerHybridMethod(std::string name, ReturnType (Derived::*method)(Args...)) {
|
|
114
|
-
if (_getters.contains(name) || _setters.contains(name)) [[unlikely]] {
|
|
115
|
-
throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a property with that name already exists!");
|
|
116
|
-
}
|
|
117
|
-
if (_methods.contains(name)) [[unlikely]] {
|
|
118
|
-
throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a method with that name already exists!");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
_methods.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionType::METHOD));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
105
|
/**
|
|
125
106
|
* Registers the given C++ method as a property getter that can be called from JS, through the object's Prototype.
|
|
126
107
|
* Example:
|
|
@@ -137,7 +118,7 @@ public:
|
|
|
137
118
|
throw std::runtime_error("Cannot add Hybrid Property Getter \"" + name + "\" - a method with that name already exists!");
|
|
138
119
|
}
|
|
139
120
|
|
|
140
|
-
_getters.emplace(name, HybridFunction::createHybridFunction(name, method,
|
|
121
|
+
_getters.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionKind::GETTER));
|
|
141
122
|
}
|
|
142
123
|
|
|
143
124
|
/**
|
|
@@ -156,7 +137,47 @@ public:
|
|
|
156
137
|
throw std::runtime_error("Cannot add Hybrid Property Setter \"" + name + "\" - a method with that name already exists!");
|
|
157
138
|
}
|
|
158
139
|
|
|
159
|
-
_setters.emplace(name, HybridFunction::createHybridFunction(name, method,
|
|
140
|
+
_setters.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionKind::SETTER));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Registers the given C++ method as a Hybrid Method that can be called from JS, through the object's Prototype.
|
|
145
|
+
* Example:
|
|
146
|
+
* ```cpp
|
|
147
|
+
* registerHybridMethod("sayHello", &MyObject::sayHello);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
template <typename Derived, typename ReturnType, typename... Args>
|
|
151
|
+
inline void registerHybridMethod(std::string name, ReturnType (Derived::*method)(Args...)) {
|
|
152
|
+
if (_getters.contains(name) || _setters.contains(name)) [[unlikely]] {
|
|
153
|
+
throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a property with that name already exists!");
|
|
154
|
+
}
|
|
155
|
+
if (_methods.contains(name)) [[unlikely]] {
|
|
156
|
+
throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a method with that name already exists!");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_methods.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionKind::METHOD));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Registers the given raw JSI C++ method as a Hybrid Method that can be called from JS, through the object's Prototype.
|
|
164
|
+
* Example:
|
|
165
|
+
* ```cpp
|
|
166
|
+
* registerRawHybridMethod("sayHello", &MyObject::sayHello);
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
template <typename Derived>
|
|
170
|
+
inline void registerRawHybridMethod(std::string name, size_t expectedArgumentsCount,
|
|
171
|
+
jsi::Value (Derived::*method)(jsi::Runtime& runtime, const jsi::Value& thisArg,
|
|
172
|
+
const jsi::Value* args, size_t count)) {
|
|
173
|
+
if (_getters.contains(name) || _setters.contains(name)) [[unlikely]] {
|
|
174
|
+
throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a property with that name already exists!");
|
|
175
|
+
}
|
|
176
|
+
if (_methods.contains(name)) [[unlikely]] {
|
|
177
|
+
throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a method with that name already exists!");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_methods.emplace(name, HybridFunction::createRawHybridFunction(name, expectedArgumentsCount, method));
|
|
160
181
|
}
|
|
161
182
|
};
|
|
162
183
|
|
|
@@ -29,9 +29,9 @@ std::vector<std::string> HybridObjectRegistry::getAllHybridObjectNames() {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
void HybridObjectRegistry::registerHybridObjectConstructor(const std::string& hybridObjectName, HybridObjectConstructorFn&& constructorFn) {
|
|
32
|
-
Logger::log(TAG, "Registering HybridObject \"%s\"...", hybridObjectName);
|
|
32
|
+
Logger::log(LogLevel::Info, TAG, "Registering HybridObject \"%s\"...", hybridObjectName.c_str());
|
|
33
33
|
auto& map = HybridObjectRegistry::getRegistry();
|
|
34
|
-
#
|
|
34
|
+
#ifndef NDEBUG
|
|
35
35
|
if (map.contains(hybridObjectName)) [[unlikely]] {
|
|
36
36
|
auto message =
|
|
37
37
|
"HybridObject \"" + std::string(hybridObjectName) +
|
|
@@ -43,7 +43,7 @@ void HybridObjectRegistry::registerHybridObjectConstructor(const std::string& hy
|
|
|
43
43
|
}
|
|
44
44
|
#endif
|
|
45
45
|
map.insert({hybridObjectName, std::move(constructorFn)});
|
|
46
|
-
Logger::log(TAG, "Successfully registered HybridObject \"%s\"!", hybridObjectName);
|
|
46
|
+
Logger::log(LogLevel::Info, TAG, "Successfully registered HybridObject \"%s\"!", hybridObjectName.c_str());
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
std::shared_ptr<HybridObject> HybridObjectRegistry::createHybridObject(const std::string& hybridObjectName) {
|
|
@@ -57,7 +57,7 @@ std::shared_ptr<HybridObject> HybridObjectRegistry::createHybridObject(const std
|
|
|
57
57
|
}
|
|
58
58
|
std::shared_ptr<HybridObject> instance = fn->second();
|
|
59
59
|
|
|
60
|
-
#
|
|
60
|
+
#ifndef NDEBUG
|
|
61
61
|
if (instance == nullptr) [[unlikely]] {
|
|
62
62
|
throw std::runtime_error("Failed to create HybridObject \"" + hybridObjectName +
|
|
63
63
|
"\" - "
|
|
@@ -11,7 +11,7 @@ static constexpr auto GLOBAL_DISPATCHER_HOLDER_NAME = "__nitroDispatcher";
|
|
|
11
11
|
std::unordered_map<jsi::Runtime*, std::weak_ptr<Dispatcher>> Dispatcher::_globalCache;
|
|
12
12
|
|
|
13
13
|
void Dispatcher::installRuntimeGlobalDispatcher(jsi::Runtime& runtime, std::shared_ptr<Dispatcher> dispatcher) {
|
|
14
|
-
Logger::log(TAG, "Installing global Dispatcher Holder into Runtime \"%s\"...", getRuntimeId(runtime));
|
|
14
|
+
Logger::log(LogLevel::Info, TAG, "Installing global Dispatcher Holder into Runtime \"%s\"...", getRuntimeId(runtime).c_str());
|
|
15
15
|
|
|
16
16
|
// Store a weak reference in global cache
|
|
17
17
|
_globalCache[&runtime] = std::weak_ptr(dispatcher);
|
|
@@ -33,14 +33,15 @@ std::shared_ptr<Dispatcher> Dispatcher::getRuntimeGlobalDispatcher(jsi::Runtime&
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
Logger::log(TAG, "Unknown Runtime (%s), looking for Dispatcher through JSI global lookup...",
|
|
36
|
+
Logger::log(LogLevel::Warning, TAG, "Unknown Runtime (%s), looking for Dispatcher through JSI global lookup...",
|
|
37
|
+
getRuntimeId(runtime).c_str());
|
|
37
38
|
jsi::Value dispatcherHolderValue = getRuntimeGlobalDispatcherHolder(runtime);
|
|
38
39
|
jsi::Object dispatcherHolder = dispatcherHolderValue.getObject(runtime);
|
|
39
40
|
return dispatcherHolder.getNativeState<Dispatcher>(runtime);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
jsi::Value Dispatcher::getRuntimeGlobalDispatcherHolder(jsi::Runtime& runtime) {
|
|
43
|
-
#
|
|
44
|
+
#ifndef NDEBUG
|
|
44
45
|
if (!runtime.global().hasProperty(runtime, GLOBAL_DISPATCHER_HOLDER_NAME)) {
|
|
45
46
|
throw std::runtime_error("Failed to get current Dispatcher - the global Dispatcher "
|
|
46
47
|
"holder (global." +
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
namespace margelo::nitro {
|
|
13
13
|
|
|
14
14
|
ThreadPool::ThreadPool(const char* name, size_t numThreads) : _isAlive(true), _name(name) {
|
|
15
|
-
Logger::log(TAG, "Creating ThreadPool \"%s\" with %i threads...", name, numThreads);
|
|
15
|
+
Logger::log(LogLevel::Info, TAG, "Creating ThreadPool \"%s\" with %i threads...", name, numThreads);
|
|
16
16
|
|
|
17
17
|
for (size_t i = 0; i < numThreads; ++i) {
|
|
18
18
|
std::string threadName = std::string(name) + "-" + std::to_string(i + 1);
|
|
@@ -56,7 +56,7 @@ void ThreadPool::run(std::function<void()>&& task) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
ThreadPool::~ThreadPool() {
|
|
59
|
-
Logger::log(TAG, "Destroying ThreadPool \"%s\"...", _name);
|
|
59
|
+
Logger::log(LogLevel::Info, TAG, "Destroying ThreadPool \"%s\"...", _name);
|
|
60
60
|
|
|
61
61
|
{
|
|
62
62
|
// Lock and set `_isAlive` to false.
|