react-native-nitro-modules 0.16.2 → 0.18.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 +8 -2
- package/README.md +6 -6
- package/android/build.gradle +10 -1
- package/android/src/main/cpp/core/JPromise.hpp +62 -5
- package/android/src/main/cpp/utils/JUnit.hpp +38 -0
- package/android/src/main/java/com/margelo/nitro/core/Promise.kt +76 -10
- package/cpp/core/HybridFunction.hpp +1 -0
- package/cpp/core/Promise.cpp +10 -0
- package/cpp/core/Promise.hpp +422 -0
- package/cpp/jsi/JSIConverter+Exception.hpp +54 -0
- package/cpp/jsi/JSIConverter+Function.hpp +5 -7
- package/cpp/jsi/JSIConverter+Future.hpp +45 -0
- package/cpp/jsi/JSIConverter+Promise.hpp +76 -62
- package/cpp/jsi/JSIConverter.hpp +2 -0
- package/cpp/platform/ThreadUtils.hpp +2 -0
- package/cpp/prototype/HybridObjectPrototype.cpp +1 -0
- package/cpp/templates/IsSharedPtrTo.hpp +0 -1
- package/cpp/threading/ThreadPool.cpp +47 -32
- package/cpp/threading/ThreadPool.hpp +15 -3
- package/cpp/utils/AssertPromiseState.hpp +34 -0
- package/cpp/utils/BorrowingReference.hpp +1 -1
- package/cpp/utils/OwningReference.hpp +2 -2
- package/cpp/utils/TypeInfo.hpp +12 -11
- package/ios/core/RuntimeError.swift +26 -1
- package/ios/turbomodule/NativeNitroModules+OldArch.mm +1 -1
- package/ios/utils/RuntimeError.hpp +30 -0
- package/ios/utils/SwiftClosure.hpp +5 -1
- package/package.json +4 -2
- package/cpp/jsi/JSPromise.cpp +0 -58
- package/cpp/jsi/JSPromise.hpp +0 -54
- package/ios/core/PromiseHolder.hpp +0 -86
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
|
-
class Dispatcher;
|
|
10
|
-
|
|
11
|
-
class JSPromise;
|
|
12
9
|
|
|
13
10
|
template <typename T, typename Enable>
|
|
14
11
|
struct JSIConverter;
|
|
@@ -16,11 +13,8 @@ struct JSIConverter;
|
|
|
16
13
|
|
|
17
14
|
#include "JSIConverter.hpp"
|
|
18
15
|
|
|
19
|
-
#include "
|
|
20
|
-
#include
|
|
21
|
-
#include "ThreadPool.hpp"
|
|
22
|
-
#include "TypeInfo.hpp"
|
|
23
|
-
#include <future>
|
|
16
|
+
#include "Promise.hpp"
|
|
17
|
+
#include <exception>
|
|
24
18
|
#include <jsi/jsi.h>
|
|
25
19
|
#include <memory>
|
|
26
20
|
|
|
@@ -28,66 +22,86 @@ namespace margelo::nitro {
|
|
|
28
22
|
|
|
29
23
|
using namespace facebook;
|
|
30
24
|
|
|
31
|
-
// std::
|
|
25
|
+
// Promise<T, std::exception> <> Promise<T>
|
|
32
26
|
template <typename TResult>
|
|
33
|
-
struct JSIConverter<std::
|
|
34
|
-
static inline std::
|
|
35
|
-
|
|
27
|
+
struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
|
|
28
|
+
static inline std::shared_ptr<Promise<TResult>> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
29
|
+
// Create new Promise and prepare onResolved / onRejected callbacks
|
|
30
|
+
auto promise = Promise<TResult>::create();
|
|
31
|
+
auto thenCallback = [&]() {
|
|
32
|
+
if constexpr (std::is_void_v<TResult>) {
|
|
33
|
+
// void: resolve()
|
|
34
|
+
return JSIConverter<std::function<void(std::monostate)>>::toJSI(runtime, [=](std::monostate) { promise->resolve(); });
|
|
35
|
+
} else {
|
|
36
|
+
// T: resolve(T)
|
|
37
|
+
return JSIConverter<std::function<void(TResult)>>::toJSI(runtime, [=](const TResult& result) { promise->resolve(result); });
|
|
38
|
+
}
|
|
39
|
+
}();
|
|
40
|
+
auto catchCallback = JSIConverter<std::function<void(std::exception_ptr)>>::toJSI(
|
|
41
|
+
runtime, [=](const std::exception_ptr& exception) { promise->reject(exception); });
|
|
42
|
+
|
|
43
|
+
// Chain .then listeners on JS Promise (onResolved and onRejected)
|
|
44
|
+
jsi::Object jsPromise = value.asObject(runtime);
|
|
45
|
+
jsi::Function thenFn = jsPromise.getPropertyAsFunction(runtime, "then");
|
|
46
|
+
thenFn.callWithThis(runtime, jsPromise, thenCallback, catchCallback);
|
|
47
|
+
|
|
48
|
+
return promise;
|
|
36
49
|
}
|
|
37
50
|
|
|
38
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Logger::log(LogLevel::Error, "JSIConverter",
|
|
54
|
-
"Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed!");
|
|
55
|
-
return;
|
|
51
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<Promise<TResult>>& promise) {
|
|
52
|
+
if (promise->isPending()) {
|
|
53
|
+
// Get Promise ctor from global
|
|
54
|
+
jsi::Function promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
|
|
55
|
+
jsi::HostFunctionType executor = [promise](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
|
|
56
|
+
size_t count) -> jsi::Value {
|
|
57
|
+
// Add resolver listener
|
|
58
|
+
if constexpr (std::is_void_v<TResult>) {
|
|
59
|
+
// It's resolving to void.
|
|
60
|
+
auto resolver = JSIConverter<std::function<void()>>::fromJSI(runtime, arguments[0]);
|
|
61
|
+
promise->addOnResolvedListener(std::move(resolver));
|
|
62
|
+
} else {
|
|
63
|
+
// It's a type.
|
|
64
|
+
auto resolver = JSIConverter<std::function<void(TResult)>>::fromJSI(runtime, arguments[0]);
|
|
65
|
+
promise->addOnResolvedListener(std::move(resolver));
|
|
56
66
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
67
|
+
// Add rejecter listener
|
|
68
|
+
auto rejecter = JSIConverter<std::function<void(std::exception_ptr)>>::fromJSI(runtime, arguments[1]);
|
|
69
|
+
promise->addOnRejectedListener(std::move(rejecter));
|
|
70
|
+
|
|
71
|
+
return jsi::Value::undefined();
|
|
72
|
+
};
|
|
73
|
+
// Call `Promise` constructor (aka create promise), and pass `executor` function
|
|
74
|
+
return promiseCtor.callAsConstructor(
|
|
75
|
+
runtime, jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "executor"), 2, executor));
|
|
76
|
+
} else if (promise->isResolved()) {
|
|
77
|
+
// Promise is already resolved - just return immediately
|
|
78
|
+
jsi::Object promiseObject = runtime.global().getPropertyAsObject(runtime, "Promise");
|
|
79
|
+
jsi::Function createResolvedPromise = promiseObject.getPropertyAsFunction(runtime, "resolve");
|
|
80
|
+
if constexpr (std::is_void_v<TResult>) {
|
|
81
|
+
// It's resolving to void.
|
|
82
|
+
return createResolvedPromise.call(runtime);
|
|
83
|
+
} else {
|
|
84
|
+
// It's resolving to a type.
|
|
85
|
+
jsi::Value result = JSIConverter<TResult>::toJSI(runtime, promise->getResult());
|
|
86
|
+
return createResolvedPromise.call(runtime, std::move(result));
|
|
87
|
+
}
|
|
88
|
+
} else if (promise->isRejected()) {
|
|
89
|
+
// Promise is already rejected - just return immediately
|
|
90
|
+
jsi::Object promiseObject = runtime.global().getPropertyAsObject(runtime, "Promise");
|
|
91
|
+
jsi::Function createRejectedPromise = promiseObject.getPropertyAsFunction(runtime, "reject");
|
|
92
|
+
jsi::Value error = JSIConverter<std::exception_ptr>::toJSI(runtime, promise->getError());
|
|
93
|
+
return createRejectedPromise.call(runtime, std::move(error));
|
|
94
|
+
} else {
|
|
95
|
+
throw std::runtime_error("Promise has invalid state!");
|
|
96
|
+
}
|
|
87
97
|
}
|
|
88
98
|
|
|
89
|
-
static inline bool canConvert(jsi::Runtime
|
|
90
|
-
|
|
99
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
100
|
+
if (!value.isObject()) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
jsi::Object object = value.getObject(runtime);
|
|
104
|
+
return object.hasProperty(runtime, "then");
|
|
91
105
|
}
|
|
92
106
|
};
|
|
93
107
|
|
package/cpp/jsi/JSIConverter.hpp
CHANGED
|
@@ -183,7 +183,9 @@ struct JSIConverter<std::string> final {
|
|
|
183
183
|
|
|
184
184
|
#include "JSIConverter+AnyMap.hpp"
|
|
185
185
|
#include "JSIConverter+ArrayBuffer.hpp"
|
|
186
|
+
#include "JSIConverter+Exception.hpp"
|
|
186
187
|
#include "JSIConverter+Function.hpp"
|
|
188
|
+
#include "JSIConverter+Future.hpp"
|
|
187
189
|
#include "JSIConverter+HostObject.hpp"
|
|
188
190
|
#include "JSIConverter+HybridObject.hpp"
|
|
189
191
|
#include "JSIConverter+Optional.hpp"
|
|
@@ -11,38 +11,58 @@
|
|
|
11
11
|
|
|
12
12
|
namespace margelo::nitro {
|
|
13
13
|
|
|
14
|
-
ThreadPool::ThreadPool(const char* name, size_t
|
|
15
|
-
|
|
14
|
+
ThreadPool::ThreadPool(const char* name, size_t initialThreadsCount, size_t maxThreadsCount)
|
|
15
|
+
: _isAlive(true), _threadCountLimit(maxThreadsCount), _name(name) {
|
|
16
|
+
Logger::log(LogLevel::Info, TAG, "Creating ThreadPool \"%s\" with %i initial threads (max: %i)...", name, initialThreadsCount,
|
|
17
|
+
maxThreadsCount);
|
|
16
18
|
|
|
17
|
-
for (size_t i = 0; i <
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
for (size_t i = 0; i < initialThreadsCount; i++) {
|
|
20
|
+
addThread();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void ThreadPool::addThread() {
|
|
25
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
26
|
+
if (!_isAlive) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
size_t i = ++_threadCount;
|
|
31
|
+
Logger::log(LogLevel::Info, TAG, "Adding Thread %i to ThreadPool \"%s\"...", i, _name);
|
|
32
|
+
|
|
33
|
+
std::string threadName = std::string(_name) + "-" + std::to_string(i);
|
|
34
|
+
_workers.emplace_back([this, threadName] {
|
|
35
|
+
// Set the Thread's name
|
|
36
|
+
ThreadUtils::setThreadName(threadName);
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
// Schedule the oldest task
|
|
35
|
-
task = std::move(_tasks.front());
|
|
36
|
-
_tasks.pop();
|
|
38
|
+
// Start the run-loop
|
|
39
|
+
while (true) {
|
|
40
|
+
std::function<void()> task;
|
|
41
|
+
{
|
|
42
|
+
// Lock on the mutex so only one Worker receives the condition signal at a time
|
|
43
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
44
|
+
this->_condition.wait(lock, [this] { return !_isAlive || !_tasks.empty(); });
|
|
45
|
+
if (!_isAlive && _tasks.empty()) {
|
|
46
|
+
// ThreadPool is dead - stop run-loop.
|
|
47
|
+
return;
|
|
37
48
|
}
|
|
38
|
-
//
|
|
39
|
-
task();
|
|
49
|
+
// Schedule the oldest task
|
|
50
|
+
task = std::move(_tasks.front());
|
|
51
|
+
_tasks.pop();
|
|
40
52
|
}
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
// Run it (outside of the mutex so others can run in parallel)
|
|
54
|
+
task();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
_condition.notify_all();
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
void ThreadPool::run(std::function<void()>&& task) {
|
|
61
|
+
// If there are tasks still waiting to be finished, just start a new Thread.
|
|
62
|
+
if (!_tasks.empty() && _threadCount < _threadCountLimit) {
|
|
63
|
+
addThread();
|
|
64
|
+
}
|
|
65
|
+
// New scope because of RAII lock
|
|
46
66
|
{
|
|
47
67
|
// lock on the mutex - we want to emplace the task back in the queue
|
|
48
68
|
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
@@ -71,13 +91,8 @@ ThreadPool::~ThreadPool() {
|
|
|
71
91
|
}
|
|
72
92
|
}
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
static
|
|
76
|
-
if (shared == nullptr) {
|
|
77
|
-
int availableThreads = std::thread::hardware_concurrency();
|
|
78
|
-
auto numThreads = std::min(availableThreads, 3);
|
|
79
|
-
shared = std::make_shared<ThreadPool>("nitro-thread", numThreads);
|
|
80
|
-
}
|
|
94
|
+
ThreadPool& ThreadPool::shared() {
|
|
95
|
+
static ThreadPool shared("nitro-thread", 3, 10);
|
|
81
96
|
return shared;
|
|
82
97
|
}
|
|
83
98
|
|
|
@@ -22,10 +22,13 @@ namespace margelo::nitro {
|
|
|
22
22
|
class ThreadPool final {
|
|
23
23
|
public:
|
|
24
24
|
/**
|
|
25
|
-
* Create a new ThreadPool with the given number of
|
|
25
|
+
* Create a new ThreadPool with the given number of minimum workers/threads.
|
|
26
|
+
* The Thread Pool can expand on the fly if it is busy.
|
|
26
27
|
*/
|
|
27
|
-
explicit ThreadPool(const char* const name, size_t
|
|
28
|
+
explicit ThreadPool(const char* const name, size_t initialThreadsCount, size_t maxThreadsCount);
|
|
28
29
|
~ThreadPool();
|
|
30
|
+
ThreadPool(const ThreadPool&) = delete;
|
|
31
|
+
ThreadPool(ThreadPool&&) = delete;
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
34
|
* Schedules the given task asynchronously on the ThreadPool.
|
|
@@ -33,12 +36,19 @@ public:
|
|
|
33
36
|
*/
|
|
34
37
|
void run(std::function<void()>&& task);
|
|
35
38
|
|
|
39
|
+
private:
|
|
40
|
+
/**
|
|
41
|
+
* Adds a new Thread to the current Thread Pool.
|
|
42
|
+
* This grows the size by one, and potentially starts work sooner if other Threads are busy.
|
|
43
|
+
*/
|
|
44
|
+
void addThread();
|
|
45
|
+
|
|
36
46
|
public:
|
|
37
47
|
/**
|
|
38
48
|
* Get a static singleton instance - a shared ThreadPool.
|
|
39
49
|
* The shared ThreadPool has 3 threads.
|
|
40
50
|
*/
|
|
41
|
-
static
|
|
51
|
+
static ThreadPool& shared();
|
|
42
52
|
|
|
43
53
|
private:
|
|
44
54
|
std::vector<std::thread> _workers;
|
|
@@ -46,6 +56,8 @@ private:
|
|
|
46
56
|
std::mutex _queueMutex;
|
|
47
57
|
std::condition_variable _condition;
|
|
48
58
|
std::atomic<bool> _isAlive;
|
|
59
|
+
std::atomic<size_t> _threadCount;
|
|
60
|
+
size_t _threadCountLimit;
|
|
49
61
|
const char* _name;
|
|
50
62
|
static constexpr auto TAG = "ThreadPool";
|
|
51
63
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//
|
|
2
|
+
// AssertPromiseState.hpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 20.11.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
namespace margelo::nitro {
|
|
11
|
+
template <typename TResult>
|
|
12
|
+
class Promise;
|
|
13
|
+
} // namespace margelo::nitro
|
|
14
|
+
|
|
15
|
+
#include "Promise.hpp"
|
|
16
|
+
#include "TypeInfo.hpp"
|
|
17
|
+
#include <exception>
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
namespace margelo::nitro {
|
|
21
|
+
|
|
22
|
+
enum PromiseTask { WANTS_TO_RESOLVE, WANTS_TO_REJECT };
|
|
23
|
+
|
|
24
|
+
template <typename TResult>
|
|
25
|
+
void assertPromiseState(Promise<TResult>& promise, PromiseTask task) {
|
|
26
|
+
if (!promise.isPending()) [[unlikely]] {
|
|
27
|
+
std::string taskString = task == WANTS_TO_RESOLVE ? "resolve" : "reject";
|
|
28
|
+
std::string state = promise.isResolved() ? "resolved" : "rejected";
|
|
29
|
+
throw std::runtime_error("Cannot " + taskString + " Promise<" + TypeInfo::getFriendlyTypename<TResult>() + "> - it is already " +
|
|
30
|
+
state + "!");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
} // namespace margelo::nitro
|
|
@@ -34,7 +34,7 @@ public:
|
|
|
34
34
|
|
|
35
35
|
explicit OwningReference(T* value)
|
|
36
36
|
: _value(value), _isDeleted(new bool(false)), _strongRefCount(new std::atomic_size_t(1)), _weakRefCount(new std::atomic_size_t(0)),
|
|
37
|
-
_mutex(new std::
|
|
37
|
+
_mutex(new std::recursive_mutex()) {}
|
|
38
38
|
|
|
39
39
|
OwningReference(const OwningReference& ref)
|
|
40
40
|
: _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
|
|
@@ -229,7 +229,7 @@ private:
|
|
|
229
229
|
bool* _isDeleted;
|
|
230
230
|
std::atomic_size_t* _strongRefCount;
|
|
231
231
|
std::atomic_size_t* _weakRefCount;
|
|
232
|
-
std::
|
|
232
|
+
std::recursive_mutex* _mutex;
|
|
233
233
|
};
|
|
234
234
|
|
|
235
235
|
} // namespace margelo::nitro
|
package/cpp/utils/TypeInfo.hpp
CHANGED
|
@@ -24,17 +24,6 @@ struct TypeInfo final {
|
|
|
24
24
|
public:
|
|
25
25
|
TypeInfo() = delete;
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
* Get the name of the currently thrown exception
|
|
29
|
-
*/
|
|
30
|
-
static inline const char* getCurrentExceptionName() {
|
|
31
|
-
#if __has_include(<cxxabi.h>)
|
|
32
|
-
return __cxxabiv1::__cxa_current_exception_type()->name();
|
|
33
|
-
#else
|
|
34
|
-
return "<unknown>";
|
|
35
|
-
#endif
|
|
36
|
-
}
|
|
37
|
-
|
|
38
27
|
static inline std::string replaceRegex(const std::string& original, const std::string& pattern, const std::string& replacement) {
|
|
39
28
|
std::regex re(pattern);
|
|
40
29
|
return std::regex_replace(original, re, replacement);
|
|
@@ -112,6 +101,18 @@ public:
|
|
|
112
101
|
std::string string = stream.str();
|
|
113
102
|
return string.substr(0, string.length() - 2);
|
|
114
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the name of the currently thrown exception
|
|
107
|
+
*/
|
|
108
|
+
static inline std::string getCurrentExceptionName() {
|
|
109
|
+
#if __has_include(<cxxabi.h>)
|
|
110
|
+
std::string name = __cxxabiv1::__cxa_current_exception_type()->name();
|
|
111
|
+
return demangleName(name);
|
|
112
|
+
#else
|
|
113
|
+
return "<unknown>";
|
|
114
|
+
#endif
|
|
115
|
+
}
|
|
115
116
|
};
|
|
116
117
|
|
|
117
118
|
} // namespace margelo::nitro
|
|
@@ -12,6 +12,31 @@ import Foundation
|
|
|
12
12
|
*
|
|
13
13
|
* Throw this error in Nitro Modules to provide clear and concise error messages to JS.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
@frozen
|
|
16
|
+
public enum RuntimeError: Error, CustomStringConvertible {
|
|
16
17
|
case error(withMessage: String)
|
|
18
|
+
|
|
19
|
+
public var description: String {
|
|
20
|
+
switch self {
|
|
21
|
+
case .error(let message): return message
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new `RuntimeError` from the given C++ `std::exception`.
|
|
27
|
+
*/
|
|
28
|
+
public static func from(cppError: std.exception_ptr) -> RuntimeError {
|
|
29
|
+
let message = margelo.nitro.get_exception_message(cppError)
|
|
30
|
+
return .error(withMessage: String(message))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public extension Error {
|
|
35
|
+
/**
|
|
36
|
+
* Converts this `Error` to a C++ `std::exception`.
|
|
37
|
+
*/
|
|
38
|
+
func toCpp() -> std.exception_ptr {
|
|
39
|
+
let message = String(describing: self)
|
|
40
|
+
return margelo.nitro.make_exception(std.string(message))
|
|
41
|
+
}
|
|
17
42
|
}
|
|
@@ -57,7 +57,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
|
|
|
57
57
|
// 5. Install Nitro
|
|
58
58
|
nitro::install(*runtime, dispatcher);
|
|
59
59
|
return nil;
|
|
60
|
-
} catch (std::exception& error) {
|
|
60
|
+
} catch (const std::exception& error) {
|
|
61
61
|
// ?. Any C++ error occurred (probably in nitro::install()?)
|
|
62
62
|
return [NSString stringWithCString:error.what() encoding:kCFStringEncodingUTF8];
|
|
63
63
|
} catch (...) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RuntimeError.hpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 19.11.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <exception>
|
|
11
|
+
#include <string>
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro {
|
|
14
|
+
|
|
15
|
+
static inline std::exception_ptr make_exception(const std::string& message) {
|
|
16
|
+
return std::make_exception_ptr(std::runtime_error(message));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static inline std::string get_exception_message(const std::exception_ptr& exception) {
|
|
20
|
+
try {
|
|
21
|
+
std::rethrow_exception(exception);
|
|
22
|
+
} catch (const std::exception& error) {
|
|
23
|
+
return error.what();
|
|
24
|
+
} catch (...) {
|
|
25
|
+
std::string errorName = TypeInfo::getCurrentExceptionName();
|
|
26
|
+
return "Unknown " + errorName + " exception";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
} // namespace margelo::nitro
|
|
@@ -55,7 +55,11 @@ public:
|
|
|
55
55
|
/**
|
|
56
56
|
* Gets the underlying `std::function`.
|
|
57
57
|
*/
|
|
58
|
-
const std::function<void()>& getFunction() {
|
|
58
|
+
const std::function<void()>& getFunction() const {
|
|
59
|
+
return _function;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
std::function<void()> getFunctionCopy() const {
|
|
59
63
|
return _function;
|
|
60
64
|
}
|
|
61
65
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-modules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"ios",
|
|
29
29
|
"android",
|
|
30
30
|
"visionOS",
|
|
31
|
+
"tvOS",
|
|
32
|
+
"macOS",
|
|
31
33
|
"cpp",
|
|
32
34
|
"framework",
|
|
33
35
|
"react",
|
|
@@ -70,7 +72,7 @@
|
|
|
70
72
|
"jest": "*",
|
|
71
73
|
"react": "*",
|
|
72
74
|
"react-native": "*",
|
|
73
|
-
"react-native-builder-bob": "^0.
|
|
75
|
+
"react-native-builder-bob": "^0.33.1"
|
|
74
76
|
},
|
|
75
77
|
"peerDependencies": {
|
|
76
78
|
"react": "*",
|
package/cpp/jsi/JSPromise.cpp
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#include "JSPromise.hpp"
|
|
2
|
-
#include "JSICache.hpp"
|
|
3
|
-
#include "NitroLogger.hpp"
|
|
4
|
-
#include <jsi/jsi.h>
|
|
5
|
-
|
|
6
|
-
namespace margelo::nitro {
|
|
7
|
-
|
|
8
|
-
using namespace facebook;
|
|
9
|
-
|
|
10
|
-
JSPromise::JSPromise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter) {
|
|
11
|
-
auto functionCache = JSICache::getOrCreateCache(runtime);
|
|
12
|
-
_resolver = functionCache.makeShared(std::move(resolver));
|
|
13
|
-
_rejecter = functionCache.makeShared(std::move(rejecter));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
jsi::Value JSPromise::createPromise(jsi::Runtime& runtime, RunPromise&& run) {
|
|
17
|
-
// Get Promise ctor from global
|
|
18
|
-
auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
|
|
19
|
-
|
|
20
|
-
auto promiseCallback = jsi::Function::createFromHostFunction(
|
|
21
|
-
runtime, jsi::PropNameID::forUtf8(runtime, "PromiseCallback"), 2,
|
|
22
|
-
[run = std::move(run)](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
23
|
-
// Get resolver and rejecter
|
|
24
|
-
auto resolver = arguments[0].getObject(runtime).getFunction(runtime);
|
|
25
|
-
auto rejecter = arguments[1].getObject(runtime).getFunction(runtime);
|
|
26
|
-
// Create `Promise` type that wraps the JSI callbacks
|
|
27
|
-
auto promise = std::make_shared<JSPromise>(runtime, std::move(resolver), std::move(rejecter));
|
|
28
|
-
// Call `run` callback
|
|
29
|
-
run(runtime, promise);
|
|
30
|
-
|
|
31
|
-
return jsi::Value::undefined();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return promiseCtor.callAsConstructor(runtime, promiseCallback);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
void JSPromise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
|
|
38
|
-
OwningLock<jsi::Function> lock = _resolver.lock();
|
|
39
|
-
|
|
40
|
-
if (!_resolver) {
|
|
41
|
-
Logger::log(LogLevel::Error, TAG, "Promise resolver function has already been deleted! Ignoring call..");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
_resolver->call(runtime, std::move(result));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void JSPromise::reject(jsi::Runtime& runtime, std::string message) {
|
|
48
|
-
OwningLock<jsi::Function> lock = _rejecter.lock();
|
|
49
|
-
|
|
50
|
-
if (!_rejecter) {
|
|
51
|
-
Logger::log(LogLevel::Error, TAG, "Promise rejecter function has already been deleted! Ignoring call..");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
jsi::JSError error(runtime, message);
|
|
55
|
-
_rejecter->call(runtime, error.value());
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
} // namespace margelo::nitro
|