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.
@@ -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&lt;T&gt;</code></td>
186
192
  <td><code>std::shared_ptr&lt;<a href="./cpp/core/Promise.hpp">Promise&lt;T&gt;</a>&gt;</code></td>
@@ -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, typename TError = std::exception>
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 TError&)>;
26
+ using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
25
27
 
26
28
  public:
27
- // Promise cannot be deleted.
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 a different error.
61
- std::string name = TypeInfo::getCurrentExceptionName();
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(TError&& error) {
86
+ static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
89
87
  auto promise = create();
90
- promise->reject(std::move(error));
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
- _result = std::move(result);
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>(_result));
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
- _result = result;
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>(_result));
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(TError&& exception) {
119
+ void reject(const std::exception_ptr& exception) {
116
120
  std::unique_lock lock(*_mutex);
117
- _result = std::move(exception);
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(std::get<TError>(_result));
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>(_result)) {
137
+ if (std::holds_alternative<TResult>(_state)) {
138
138
  // Promise is already resolved! Call the callback immediately
139
- onResolved(std::get<TResult>(_result));
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>(_result)) {
147
+ if (std::holds_alternative<TResult>(_state)) {
148
148
  // Promise is already resolved! Call the callback immediately
149
- onResolved(std::get<TResult>(_result));
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>(_result)) {
157
+ if (std::holds_alternative<TResult>(_state)) {
158
158
  // Promise is already resolved! Call the callback immediately
159
- onResolved(std::get<TResult>(_result));
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<TError>(_result)) {
172
+ if (std::holds_alternative<std::exception_ptr>(_state)) {
173
173
  // Promise is already rejected! Call the callback immediately
174
- onRejected(std::get<TError>(_result));
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<TError>(_result)) {
182
+ if (std::holds_alternative<std::exception_ptr>(_state)) {
183
183
  // Promise is already rejected! Call the callback immediately
184
- onRejected(std::get<TError>(_result));
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>(_result);
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 TError& getError() {
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<TError>(_result);
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>(_result);
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<TError>(_result);
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>(_result);
244
+ return std::holds_alternative<std::monostate>(_state);
234
245
  }
235
246
 
236
247
  private:
237
- std::variant<std::monostate, TResult, TError> _result;
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 <typename TError>
245
- class Promise<void, TError> final {
255
+ template <>
256
+ class Promise<void> final {
246
257
  public:
247
258
  using OnResolvedFunc = std::function<void()>;
248
- using OnRejectedFunc = std::function<void(const TError&)>;
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 a different error.
276
- std::string name = TypeInfo::getCurrentExceptionName();
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(TError&& error) {
300
+ static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
294
301
  auto promise = create();
295
- promise->reject(std::move(error));
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(TError&& exception) {
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(_error.value());
324
+ onRejected(exception);
319
325
  }
320
326
  }
321
327
 
@@ -356,7 +362,15 @@ public:
356
362
  }
357
363
 
358
364
  public:
359
- inline const TError& getError() {
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<TError> _error;
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::exception <> Error
23
+ // std::exception_ptr <> Error
23
24
  template <>
24
- struct JSIConverter<std::exception> final {
25
- static inline std::exception fromJSI(jsi::Runtime& runtime, const jsi::Value& error) {
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::exception& exception) {
32
- jsi::JSError error(runtime, exception.what());
33
- return jsi::Value(runtime, error.value());
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([&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() {
60
- callJSFunction(runtime, sharedFunction, args...);
61
- });
59
+ dispatcher->runAsync(
60
+ [&runtime, sharedFunction, ... args = std::move(args)]() { callJSFunction(runtime, sharedFunction, args...); });
62
61
  } else {
63
- return dispatcher->runAsyncAwaitable<ResultingType>(
64
- [&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() -> ResultingType {
65
- return callJSFunction(runtime, sharedFunction, args...);
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::exception)>>::toJSI(
41
- runtime, [=](const std::exception& exception) { promise->reject(exception); });
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::exception)>>::fromJSI(runtime, arguments[1]);
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::exception>::toJSI(runtime, promise->getError());
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!");
@@ -5,6 +5,8 @@
5
5
  // Created by Marc Rousavy on 14.07.24.
6
6
  //
7
7
 
8
+ #pragma once
9
+
8
10
  #include <string>
9
11
 
10
12
  namespace margelo::nitro {
@@ -7,7 +7,6 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include <jsi/jsi.h>
11
10
  #include <type_traits>
12
11
 
13
12
  namespace margelo::nitro {
@@ -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
@@ -111,7 +111,7 @@ private:
111
111
  bool* _isDeleted;
112
112
  std::atomic_size_t* _strongRefCount;
113
113
  std::atomic_size_t* _weakRefCount;
114
- std::mutex* _mutex;
114
+ std::recursive_mutex* _mutex;
115
115
  };
116
116
 
117
117
  } // 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::mutex()) {}
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::mutex* _mutex;
232
+ std::recursive_mutex* _mutex;
233
233
  };
234
234
 
235
235
  } // namespace margelo::nitro
@@ -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(), NULL, NULL, &status);
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
- public enum RuntimeError: Error {
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.exception) -> RuntimeError {
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.exception {
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::exception make_exception(const std::string& message) {
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::exception& exception) {
20
- return exception.what();
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.17.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.32.0"
75
+ "react-native-builder-bob": "^0.33.1"
74
76
  },
75
77
  "peerDependencies": {
76
78
  "react": "*",