react-native-nitro-modules 0.17.0 → 0.18.1
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 +11 -3
- package/README.md +6 -0
- package/cpp/core/Promise.hpp +78 -64
- package/cpp/jsi/JSIConverter+Exception.hpp +17 -7
- package/cpp/jsi/JSIConverter+Function.hpp +5 -7
- package/cpp/jsi/JSIConverter+Promise.hpp +4 -4
- package/cpp/platform/ThreadUtils.hpp +2 -0
- package/cpp/templates/IsSharedPtrTo.hpp +0 -1
- 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 +18 -12
- package/ios/core/RuntimeError.swift +11 -4
- package/ios/turbomodule/NativeNitroModules+OldArch.mm +1 -1
- package/ios/utils/RuntimeError.hpp +12 -4
- package/package.json +4 -2
package/NitroModules.podspec
CHANGED
|
@@ -11,11 +11,14 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.homepage = package["homepage"]
|
|
12
12
|
s.license = package["license"]
|
|
13
13
|
s.authors = package["author"]
|
|
14
|
-
|
|
15
|
-
s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 }
|
|
16
14
|
s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
|
|
15
|
+
s.platforms = {
|
|
16
|
+
:ios => min_ios_version_supported,
|
|
17
|
+
:visionos => 1.0,
|
|
18
|
+
:macos => 10.13,
|
|
19
|
+
:tvos => 13.4,
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
# VisionCamera Core C++ bindings
|
|
19
22
|
s.source_files = [
|
|
20
23
|
# Shared C++ codebase
|
|
21
24
|
"cpp/**/*.{h,hpp}",
|
|
@@ -36,6 +39,7 @@ Pod::Spec.new do |s|
|
|
|
36
39
|
"cpp/entrypoint/InstallNitro.hpp",
|
|
37
40
|
"cpp/registry/HybridObjectRegistry.hpp",
|
|
38
41
|
"cpp/jsi/JSIConverter.hpp",
|
|
42
|
+
"cpp/platform/NitroLogger.hpp",
|
|
39
43
|
"cpp/threading/Dispatcher.hpp",
|
|
40
44
|
"cpp/utils/NitroHash.hpp",
|
|
41
45
|
"cpp/utils/NitroDefines.hpp",
|
|
@@ -54,7 +58,11 @@ Pod::Spec.new do |s|
|
|
|
54
58
|
"SWIFT_OBJC_INTEROP_MODE" => "objcxx",
|
|
55
59
|
# Enables stricter modular headers
|
|
56
60
|
"DEFINES_MODULE" => "YES",
|
|
61
|
+
# C++ compiler flags, mainly for folly.
|
|
62
|
+
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
|
|
57
63
|
}
|
|
58
64
|
|
|
65
|
+
s.dependency 'React-jsi'
|
|
66
|
+
s.dependency 'React-callinvoker'
|
|
59
67
|
install_modules_dependencies(s)
|
|
60
68
|
end
|
package/README.md
CHANGED
|
@@ -181,6 +181,12 @@ The following C++ / JS types are supported out of the box:
|
|
|
181
181
|
<td>❌</td>
|
|
182
182
|
<td>❌</td>
|
|
183
183
|
</tr>
|
|
184
|
+
<tr>
|
|
185
|
+
<td><code>Error</code></td>
|
|
186
|
+
<td><code>std::exception_ptr</code></td>
|
|
187
|
+
<td><code>Error</code></td>
|
|
188
|
+
<td><code>Throwable</code></td>
|
|
189
|
+
</tr>
|
|
184
190
|
<tr>
|
|
185
191
|
<td><code>Promise<T></code></td>
|
|
186
192
|
<td><code>std::shared_ptr<<a href="./cpp/core/Promise.hpp">Promise<T></a>></code></td>
|
package/cpp/core/Promise.hpp
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
#pragma once
|
|
6
6
|
|
|
7
|
+
#include "AssertPromiseState.hpp"
|
|
8
|
+
#include "NitroDefines.hpp"
|
|
7
9
|
#include "ThreadPool.hpp"
|
|
8
10
|
#include "TypeInfo.hpp"
|
|
9
11
|
#include <exception>
|
|
@@ -17,14 +19,14 @@ namespace margelo::nitro {
|
|
|
17
19
|
|
|
18
20
|
using namespace facebook;
|
|
19
21
|
|
|
20
|
-
template <typename TResult
|
|
22
|
+
template <typename TResult>
|
|
21
23
|
class Promise final {
|
|
22
24
|
public:
|
|
23
25
|
using OnResolvedFunc = std::function<void(const TResult&)>;
|
|
24
|
-
using OnRejectedFunc = std::function<void(const
|
|
26
|
+
using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
|
|
25
27
|
|
|
26
28
|
public:
|
|
27
|
-
// Promise cannot be
|
|
29
|
+
// Promise cannot be copied.
|
|
28
30
|
Promise(const Promise&) = delete;
|
|
29
31
|
// Promise can be moved.
|
|
30
32
|
Promise(Promise&&) = default;
|
|
@@ -53,13 +55,9 @@ public:
|
|
|
53
55
|
// Run the code, then resolve.
|
|
54
56
|
TResult result = run();
|
|
55
57
|
promise->resolve(std::move(result));
|
|
56
|
-
} catch (const TError& exception) {
|
|
57
|
-
// It threw an std::exception.
|
|
58
|
-
promise->reject(exception);
|
|
59
58
|
} catch (...) {
|
|
60
|
-
// It threw
|
|
61
|
-
std::
|
|
62
|
-
promise->reject(std::runtime_error("Unknown non-std error! Name: " + name));
|
|
59
|
+
// It threw an error.
|
|
60
|
+
promise->reject(std::current_exception());
|
|
63
61
|
}
|
|
64
62
|
});
|
|
65
63
|
return promise;
|
|
@@ -85,9 +83,9 @@ public:
|
|
|
85
83
|
/**
|
|
86
84
|
* Creates an immediately rejected Promise.
|
|
87
85
|
*/
|
|
88
|
-
static std::shared_ptr<Promise> rejected(
|
|
86
|
+
static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
|
|
89
87
|
auto promise = create();
|
|
90
|
-
promise->reject(
|
|
88
|
+
promise->reject(error);
|
|
91
89
|
return promise;
|
|
92
90
|
}
|
|
93
91
|
|
|
@@ -97,33 +95,35 @@ public:
|
|
|
97
95
|
*/
|
|
98
96
|
void resolve(TResult&& result) {
|
|
99
97
|
std::unique_lock lock(*_mutex);
|
|
100
|
-
|
|
98
|
+
#ifdef NITRO_DEBUG
|
|
99
|
+
assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
|
|
100
|
+
#endif
|
|
101
|
+
_state = std::move(result);
|
|
101
102
|
for (const auto& onResolved : _onResolvedListeners) {
|
|
102
|
-
onResolved(std::get<TResult>(
|
|
103
|
+
onResolved(std::get<TResult>(_state));
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
void resolve(const TResult& result) {
|
|
106
107
|
std::unique_lock lock(*_mutex);
|
|
107
|
-
|
|
108
|
+
#ifdef NITRO_DEBUG
|
|
109
|
+
assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
|
|
110
|
+
#endif
|
|
111
|
+
_state = result;
|
|
108
112
|
for (const auto& onResolved : _onResolvedListeners) {
|
|
109
|
-
onResolved(std::get<TResult>(
|
|
113
|
+
onResolved(std::get<TResult>(_state));
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
/**
|
|
113
117
|
* Rejects this Promise with the given error, and calls any pending listeners.
|
|
114
118
|
*/
|
|
115
|
-
void reject(
|
|
119
|
+
void reject(const std::exception_ptr& exception) {
|
|
116
120
|
std::unique_lock lock(*_mutex);
|
|
117
|
-
|
|
121
|
+
#ifdef NITRO_DEBUG
|
|
122
|
+
assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
|
|
123
|
+
#endif
|
|
124
|
+
_state = exception;
|
|
118
125
|
for (const auto& onRejected : _onRejectedListeners) {
|
|
119
|
-
onRejected(
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
void reject(const TError& exception) {
|
|
123
|
-
std::unique_lock lock(*_mutex);
|
|
124
|
-
_result = exception;
|
|
125
|
-
for (const auto& onRejected : _onRejectedListeners) {
|
|
126
|
-
onRejected(std::get<TError>(_result));
|
|
126
|
+
onRejected(exception);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -134,9 +134,9 @@ public:
|
|
|
134
134
|
*/
|
|
135
135
|
void addOnResolvedListener(OnResolvedFunc&& onResolved) {
|
|
136
136
|
std::unique_lock lock(*_mutex);
|
|
137
|
-
if (std::holds_alternative<TResult>(
|
|
137
|
+
if (std::holds_alternative<TResult>(_state)) {
|
|
138
138
|
// Promise is already resolved! Call the callback immediately
|
|
139
|
-
onResolved(std::get<TResult>(
|
|
139
|
+
onResolved(std::get<TResult>(_state));
|
|
140
140
|
} else {
|
|
141
141
|
// Promise is not yet resolved, put the listener in our queue.
|
|
142
142
|
_onResolvedListeners.push_back(std::move(onResolved));
|
|
@@ -144,9 +144,9 @@ public:
|
|
|
144
144
|
}
|
|
145
145
|
void addOnResolvedListener(const OnResolvedFunc& onResolved) {
|
|
146
146
|
std::unique_lock lock(*_mutex);
|
|
147
|
-
if (std::holds_alternative<TResult>(
|
|
147
|
+
if (std::holds_alternative<TResult>(_state)) {
|
|
148
148
|
// Promise is already resolved! Call the callback immediately
|
|
149
|
-
onResolved(std::get<TResult>(
|
|
149
|
+
onResolved(std::get<TResult>(_state));
|
|
150
150
|
} else {
|
|
151
151
|
// Promise is not yet resolved, put the listener in our queue.
|
|
152
152
|
_onResolvedListeners.push_back(onResolved);
|
|
@@ -154,9 +154,9 @@ public:
|
|
|
154
154
|
}
|
|
155
155
|
void addOnResolvedListenerCopy(const std::function<void(TResult)>& onResolved) {
|
|
156
156
|
std::unique_lock lock(*_mutex);
|
|
157
|
-
if (std::holds_alternative<TResult>(
|
|
157
|
+
if (std::holds_alternative<TResult>(_state)) {
|
|
158
158
|
// Promise is already resolved! Call the callback immediately
|
|
159
|
-
onResolved(std::get<TResult>(
|
|
159
|
+
onResolved(std::get<TResult>(_state));
|
|
160
160
|
} else {
|
|
161
161
|
// Promise is not yet resolved, put the listener in our queue.
|
|
162
162
|
_onResolvedListeners.push_back(onResolved);
|
|
@@ -169,9 +169,9 @@ public:
|
|
|
169
169
|
*/
|
|
170
170
|
void addOnRejectedListener(OnRejectedFunc&& onRejected) {
|
|
171
171
|
std::unique_lock lock(*_mutex);
|
|
172
|
-
if (std::holds_alternative<
|
|
172
|
+
if (std::holds_alternative<std::exception_ptr>(_state)) {
|
|
173
173
|
// Promise is already rejected! Call the callback immediately
|
|
174
|
-
onRejected(std::get<
|
|
174
|
+
onRejected(std::get<std::exception_ptr>(_state));
|
|
175
175
|
} else {
|
|
176
176
|
// Promise is not yet rejected, put the listener in our queue.
|
|
177
177
|
_onRejectedListeners.push_back(std::move(onRejected));
|
|
@@ -179,15 +179,26 @@ public:
|
|
|
179
179
|
}
|
|
180
180
|
void addOnRejectedListener(const OnRejectedFunc& onRejected) {
|
|
181
181
|
std::unique_lock lock(*_mutex);
|
|
182
|
-
if (std::holds_alternative<
|
|
182
|
+
if (std::holds_alternative<std::exception_ptr>(_state)) {
|
|
183
183
|
// Promise is already rejected! Call the callback immediately
|
|
184
|
-
onRejected(std::get<
|
|
184
|
+
onRejected(std::get<std::exception_ptr>(_state));
|
|
185
185
|
} else {
|
|
186
186
|
// Promise is not yet rejected, put the listener in our queue.
|
|
187
187
|
_onRejectedListeners.push_back(onRejected);
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
public:
|
|
192
|
+
/**
|
|
193
|
+
* Gets an awaitable `std::future<T>` for this `Promise<T>`.
|
|
194
|
+
*/
|
|
195
|
+
std::future<TResult> await() {
|
|
196
|
+
auto promise = std::make_shared<std::promise<TResult>>();
|
|
197
|
+
addOnResolvedListener([promise](const TResult& result) { promise->set_value(result); });
|
|
198
|
+
addOnRejectedListener([promise](const std::exception_ptr& error) { promise->set_exception(error); });
|
|
199
|
+
return promise->get_future();
|
|
200
|
+
}
|
|
201
|
+
|
|
191
202
|
public:
|
|
192
203
|
/**
|
|
193
204
|
* Get the result of the Promise if it has been resolved.
|
|
@@ -197,17 +208,17 @@ public:
|
|
|
197
208
|
if (!isResolved()) {
|
|
198
209
|
throw std::runtime_error("Cannot get result when Promise is not yet resolved!");
|
|
199
210
|
}
|
|
200
|
-
return std::get<TResult>(
|
|
211
|
+
return std::get<TResult>(_state);
|
|
201
212
|
}
|
|
202
213
|
/**
|
|
203
214
|
* Get the error of the Promise if it has been rejected.
|
|
204
215
|
* If the Promise is not rejected, this will throw.
|
|
205
216
|
*/
|
|
206
|
-
inline const
|
|
217
|
+
inline const std::exception_ptr& getError() {
|
|
207
218
|
if (!isRejected()) {
|
|
208
219
|
throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
|
|
209
220
|
}
|
|
210
|
-
return std::get<
|
|
221
|
+
return std::get<std::exception_ptr>(_state);
|
|
211
222
|
}
|
|
212
223
|
|
|
213
224
|
public:
|
|
@@ -216,36 +227,36 @@ public:
|
|
|
216
227
|
*/
|
|
217
228
|
[[nodiscard]]
|
|
218
229
|
inline bool isResolved() const noexcept {
|
|
219
|
-
return std::holds_alternative<TResult>(
|
|
230
|
+
return std::holds_alternative<TResult>(_state);
|
|
220
231
|
}
|
|
221
232
|
/**
|
|
222
233
|
* Gets whether this Promise has been rejected with an error, or not.
|
|
223
234
|
*/
|
|
224
235
|
[[nodiscard]]
|
|
225
236
|
inline bool isRejected() const noexcept {
|
|
226
|
-
return std::holds_alternative<
|
|
237
|
+
return std::holds_alternative<std::exception_ptr>(_state);
|
|
227
238
|
}
|
|
228
239
|
/**
|
|
229
240
|
* Gets whether this Promise has not yet been resolved nor rejected.
|
|
230
241
|
*/
|
|
231
242
|
[[nodiscard]]
|
|
232
243
|
inline bool isPending() const noexcept {
|
|
233
|
-
return std::holds_alternative<std::monostate>(
|
|
244
|
+
return std::holds_alternative<std::monostate>(_state);
|
|
234
245
|
}
|
|
235
246
|
|
|
236
247
|
private:
|
|
237
|
-
std::variant<std::monostate, TResult,
|
|
248
|
+
std::variant<std::monostate, TResult, std::exception_ptr> _state;
|
|
238
249
|
std::vector<OnResolvedFunc> _onResolvedListeners;
|
|
239
250
|
std::vector<OnRejectedFunc> _onRejectedListeners;
|
|
240
251
|
std::unique_ptr<std::mutex> _mutex;
|
|
241
252
|
};
|
|
242
253
|
|
|
243
254
|
// Specialization for void
|
|
244
|
-
template
|
|
245
|
-
class Promise<void
|
|
255
|
+
template <>
|
|
256
|
+
class Promise<void> final {
|
|
246
257
|
public:
|
|
247
258
|
using OnResolvedFunc = std::function<void()>;
|
|
248
|
-
using OnRejectedFunc = std::function<void(const
|
|
259
|
+
using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
|
|
249
260
|
|
|
250
261
|
public:
|
|
251
262
|
Promise(const Promise&) = delete;
|
|
@@ -268,13 +279,9 @@ public:
|
|
|
268
279
|
// Run the code, then resolve.
|
|
269
280
|
run();
|
|
270
281
|
promise->resolve();
|
|
271
|
-
} catch (const TError& exception) {
|
|
272
|
-
// It threw an std::exception.
|
|
273
|
-
promise->reject(exception);
|
|
274
282
|
} catch (...) {
|
|
275
|
-
// It threw
|
|
276
|
-
std::
|
|
277
|
-
promise->reject(std::runtime_error("Unknown non-std error! Name: " + name));
|
|
283
|
+
// It threw an error.
|
|
284
|
+
promise->reject(std::current_exception());
|
|
278
285
|
}
|
|
279
286
|
});
|
|
280
287
|
return promise;
|
|
@@ -290,32 +297,31 @@ public:
|
|
|
290
297
|
promise->resolve();
|
|
291
298
|
return promise;
|
|
292
299
|
}
|
|
293
|
-
static std::shared_ptr<Promise> rejected(
|
|
300
|
+
static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
|
|
294
301
|
auto promise = create();
|
|
295
|
-
promise->reject(
|
|
302
|
+
promise->reject(error);
|
|
296
303
|
return promise;
|
|
297
304
|
}
|
|
298
305
|
|
|
299
306
|
public:
|
|
300
307
|
void resolve() {
|
|
301
308
|
std::unique_lock lock(*_mutex);
|
|
309
|
+
#ifdef NITRO_DEBUG
|
|
310
|
+
assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
|
|
311
|
+
#endif
|
|
302
312
|
_isResolved = true;
|
|
303
313
|
for (const auto& onResolved : _onResolvedListeners) {
|
|
304
314
|
onResolved();
|
|
305
315
|
}
|
|
306
316
|
}
|
|
307
|
-
void reject(
|
|
308
|
-
std::unique_lock lock(*_mutex);
|
|
309
|
-
_error = std::move(exception);
|
|
310
|
-
for (const auto& onRejected : _onRejectedListeners) {
|
|
311
|
-
onRejected(_error.value());
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
void reject(const TError& exception) {
|
|
317
|
+
void reject(const std::exception_ptr& exception) {
|
|
315
318
|
std::unique_lock lock(*_mutex);
|
|
319
|
+
#ifdef NITRO_DEBUG
|
|
320
|
+
assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
|
|
321
|
+
#endif
|
|
316
322
|
_error = exception;
|
|
317
323
|
for (const auto& onRejected : _onRejectedListeners) {
|
|
318
|
-
onRejected(
|
|
324
|
+
onRejected(exception);
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
327
|
|
|
@@ -356,7 +362,15 @@ public:
|
|
|
356
362
|
}
|
|
357
363
|
|
|
358
364
|
public:
|
|
359
|
-
|
|
365
|
+
std::future<void> await() {
|
|
366
|
+
auto promise = std::make_shared<std::promise<void>>();
|
|
367
|
+
addOnResolvedListener([promise]() { promise->set_value(); });
|
|
368
|
+
addOnRejectedListener([promise](const std::exception_ptr& error) { promise->set_exception(error); });
|
|
369
|
+
return promise->get_future();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
public:
|
|
373
|
+
inline const std::exception_ptr& getError() {
|
|
360
374
|
if (!isRejected()) {
|
|
361
375
|
throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
|
|
362
376
|
}
|
|
@@ -380,7 +394,7 @@ public:
|
|
|
380
394
|
private:
|
|
381
395
|
std::unique_ptr<std::mutex> _mutex;
|
|
382
396
|
bool _isResolved = false;
|
|
383
|
-
std::optional<
|
|
397
|
+
std::optional<std::exception_ptr> _error;
|
|
384
398
|
std::vector<OnResolvedFunc> _onResolvedListeners;
|
|
385
399
|
std::vector<OnRejectedFunc> _onRejectedListeners;
|
|
386
400
|
};
|
|
@@ -12,6 +12,7 @@ struct JSIConverter;
|
|
|
12
12
|
|
|
13
13
|
#include "JSIConverter.hpp"
|
|
14
14
|
|
|
15
|
+
#include "TypeInfo.hpp"
|
|
15
16
|
#include <exception>
|
|
16
17
|
#include <jsi/jsi.h>
|
|
17
18
|
|
|
@@ -19,18 +20,27 @@ namespace margelo::nitro {
|
|
|
19
20
|
|
|
20
21
|
using namespace facebook;
|
|
21
22
|
|
|
22
|
-
// std::
|
|
23
|
+
// std::exception_ptr <> Error
|
|
23
24
|
template <>
|
|
24
|
-
struct JSIConverter<std::
|
|
25
|
-
static inline std::
|
|
25
|
+
struct JSIConverter<std::exception_ptr> final {
|
|
26
|
+
static inline std::exception_ptr fromJSI(jsi::Runtime& runtime, const jsi::Value& error) {
|
|
26
27
|
jsi::Object object = error.asObject(runtime);
|
|
27
28
|
std::string name = object.getProperty(runtime, "name").asString(runtime).utf8(runtime);
|
|
28
29
|
std::string message = object.getProperty(runtime, "message").asString(runtime).utf8(runtime);
|
|
29
|
-
return std::runtime_error(name + ": " + message);
|
|
30
|
+
return std::make_exception_ptr(std::runtime_error(name + ": " + message));
|
|
30
31
|
}
|
|
31
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::exception_ptr& exception) {
|
|
33
|
+
try {
|
|
34
|
+
std::rethrow_exception(exception);
|
|
35
|
+
} catch (const std::exception& e) {
|
|
36
|
+
jsi::JSError error(runtime, e.what());
|
|
37
|
+
return jsi::Value(runtime, error.value());
|
|
38
|
+
} catch (...) {
|
|
39
|
+
// Some unknown exception was thrown - maybe an Objective-C error?
|
|
40
|
+
std::string errorName = TypeInfo::getCurrentExceptionName();
|
|
41
|
+
jsi::JSError error(runtime, "Unknown " + errorName + " error.");
|
|
42
|
+
return jsi::Value(runtime, error.value());
|
|
43
|
+
}
|
|
34
44
|
}
|
|
35
45
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
36
46
|
if (!value.isObject()) {
|
|
@@ -56,14 +56,12 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if constexpr (std::is_void_v<ResultingType>) {
|
|
59
|
-
dispatcher->runAsync(
|
|
60
|
-
|
|
61
|
-
});
|
|
59
|
+
dispatcher->runAsync(
|
|
60
|
+
[&runtime, sharedFunction, ... args = std::move(args)]() { callJSFunction(runtime, sharedFunction, args...); });
|
|
62
61
|
} else {
|
|
63
|
-
return dispatcher->runAsyncAwaitable<ResultingType>(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
62
|
+
return dispatcher->runAsyncAwaitable<ResultingType>([&runtime, sharedFunction, ... args = std::move(args)]() -> ResultingType {
|
|
63
|
+
return callJSFunction(runtime, sharedFunction, args...);
|
|
64
|
+
});
|
|
67
65
|
}
|
|
68
66
|
};
|
|
69
67
|
}
|
|
@@ -37,8 +37,8 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
|
|
|
37
37
|
return JSIConverter<std::function<void(TResult)>>::toJSI(runtime, [=](const TResult& result) { promise->resolve(result); });
|
|
38
38
|
}
|
|
39
39
|
}();
|
|
40
|
-
auto catchCallback = JSIConverter<std::function<void(std::
|
|
41
|
-
runtime, [=](const std::
|
|
40
|
+
auto catchCallback = JSIConverter<std::function<void(std::exception_ptr)>>::toJSI(
|
|
41
|
+
runtime, [=](const std::exception_ptr& exception) { promise->reject(exception); });
|
|
42
42
|
|
|
43
43
|
// Chain .then listeners on JS Promise (onResolved and onRejected)
|
|
44
44
|
jsi::Object jsPromise = value.asObject(runtime);
|
|
@@ -65,7 +65,7 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
|
|
|
65
65
|
promise->addOnResolvedListener(std::move(resolver));
|
|
66
66
|
}
|
|
67
67
|
// Add rejecter listener
|
|
68
|
-
auto rejecter = JSIConverter<std::function<void(std::
|
|
68
|
+
auto rejecter = JSIConverter<std::function<void(std::exception_ptr)>>::fromJSI(runtime, arguments[1]);
|
|
69
69
|
promise->addOnRejectedListener(std::move(rejecter));
|
|
70
70
|
|
|
71
71
|
return jsi::Value::undefined();
|
|
@@ -89,7 +89,7 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
|
|
|
89
89
|
// Promise is already rejected - just return immediately
|
|
90
90
|
jsi::Object promiseObject = runtime.global().getPropertyAsObject(runtime, "Promise");
|
|
91
91
|
jsi::Function createRejectedPromise = promiseObject.getPropertyAsFunction(runtime, "reject");
|
|
92
|
-
jsi::Value error = JSIConverter<std::
|
|
92
|
+
jsi::Value error = JSIConverter<std::exception_ptr>::toJSI(runtime, promise->getError());
|
|
93
93
|
return createRejectedPromise.call(runtime, std::move(error));
|
|
94
94
|
} else {
|
|
95
95
|
throw std::runtime_error("Promise has invalid state!");
|
|
@@ -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);
|
|
@@ -46,13 +35,18 @@ public:
|
|
|
46
35
|
std::string name = typeName;
|
|
47
36
|
#if __has_include(<cxxabi.h>)
|
|
48
37
|
int status = 0;
|
|
49
|
-
char* demangled_name = abi::__cxa_demangle(name.c_str(),
|
|
38
|
+
char* demangled_name = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
|
|
50
39
|
if (demangled_name != nullptr) {
|
|
51
40
|
name = demangled_name;
|
|
52
41
|
std::free(demangled_name);
|
|
53
42
|
}
|
|
54
43
|
#endif
|
|
55
44
|
|
|
45
|
+
#ifdef ANDROID
|
|
46
|
+
// std::__ndk1 -> std::__1
|
|
47
|
+
name = replaceRegex(name, R"(std::__ndk1)", "std::__1");
|
|
48
|
+
#endif
|
|
49
|
+
|
|
56
50
|
// Make a few edge-cases nicer.
|
|
57
51
|
name = replaceRegex(name, R"(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>)", "std::string");
|
|
58
52
|
name = replaceRegex(name, R"(std::__1::vector<([^>]+), std::__1::allocator<\1>>)", "std::vector<$1>");
|
|
@@ -112,6 +106,18 @@ public:
|
|
|
112
106
|
std::string string = stream.str();
|
|
113
107
|
return string.substr(0, string.length() - 2);
|
|
114
108
|
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get the name of the currently thrown exception
|
|
112
|
+
*/
|
|
113
|
+
static inline std::string getCurrentExceptionName() {
|
|
114
|
+
#if __has_include(<cxxabi.h>)
|
|
115
|
+
std::string name = __cxxabiv1::__cxa_current_exception_type()->name();
|
|
116
|
+
return demangleName(name);
|
|
117
|
+
#else
|
|
118
|
+
return "<unknown>";
|
|
119
|
+
#endif
|
|
120
|
+
}
|
|
115
121
|
};
|
|
116
122
|
|
|
117
123
|
} // namespace margelo::nitro
|
|
@@ -12,13 +12,20 @@ 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)
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
public var description: String {
|
|
20
|
+
switch self {
|
|
21
|
+
case .error(let message): return message
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* Creates a new `RuntimeError` from the given C++ `std::exception`.
|
|
20
27
|
*/
|
|
21
|
-
public static func from(cppError: std.
|
|
28
|
+
public static func from(cppError: std.exception_ptr) -> RuntimeError {
|
|
22
29
|
let message = margelo.nitro.get_exception_message(cppError)
|
|
23
30
|
return .error(withMessage: String(message))
|
|
24
31
|
}
|
|
@@ -28,7 +35,7 @@ public extension Error {
|
|
|
28
35
|
/**
|
|
29
36
|
* Converts this `Error` to a C++ `std::exception`.
|
|
30
37
|
*/
|
|
31
|
-
func toCpp() -> std.
|
|
38
|
+
func toCpp() -> std.exception_ptr {
|
|
32
39
|
let message = String(describing: self)
|
|
33
40
|
return margelo.nitro.make_exception(std.string(message))
|
|
34
41
|
}
|
|
@@ -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 (...) {
|
|
@@ -7,17 +7,25 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include "TypeInfo.hpp"
|
|
10
11
|
#include <exception>
|
|
11
12
|
#include <string>
|
|
12
13
|
|
|
13
14
|
namespace margelo::nitro {
|
|
14
15
|
|
|
15
|
-
static inline std::
|
|
16
|
-
return std::runtime_error(message);
|
|
16
|
+
static inline std::exception_ptr make_exception(const std::string& message) {
|
|
17
|
+
return std::make_exception_ptr(std::runtime_error(message));
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
static inline std::string get_exception_message(const std::
|
|
20
|
-
|
|
20
|
+
static inline std::string get_exception_message(const std::exception_ptr& exception) {
|
|
21
|
+
try {
|
|
22
|
+
std::rethrow_exception(exception);
|
|
23
|
+
} catch (const std::exception& error) {
|
|
24
|
+
return error.what();
|
|
25
|
+
} catch (...) {
|
|
26
|
+
std::string errorName = TypeInfo::getCurrentExceptionName();
|
|
27
|
+
return "Unknown " + errorName + " exception";
|
|
28
|
+
}
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
} // namespace margelo::nitro
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-modules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
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": "*",
|