react-native-nitro-modules 0.22.1 → 0.24.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.
Files changed (60) hide show
  1. package/NitroModules.podspec +1 -0
  2. package/README.md +6 -0
  3. package/android/build.gradle +1 -1
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/cpp/registry/DefaultConstructableObject.hpp +1 -1
  6. package/android/src/main/java/com/margelo/nitro/views/HybridView.kt +11 -0
  7. package/cpp/core/ArrayBuffer.cpp +3 -5
  8. package/cpp/core/ArrayBuffer.hpp +2 -2
  9. package/cpp/core/HybridFunction.hpp +5 -1
  10. package/cpp/core/HybridObject.cpp +3 -1
  11. package/cpp/core/Promise.hpp +11 -8
  12. package/cpp/jsi/JSICache.hpp +3 -3
  13. package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +1 -1
  14. package/cpp/jsi/JSIConverter+Exception.hpp +4 -1
  15. package/cpp/jsi/JSIConverter+Function.hpp +24 -67
  16. package/cpp/jsi/JSIConverter+Promise.hpp +3 -2
  17. package/cpp/prototype/HybridObjectPrototype.cpp +12 -9
  18. package/cpp/registry/HybridObjectRegistry.cpp +3 -0
  19. package/cpp/templates/PromiseType.hpp +12 -0
  20. package/cpp/threading/Dispatcher.cpp +7 -5
  21. package/cpp/utils/BorrowingReference.hpp +40 -43
  22. package/cpp/utils/JSCallback.hpp +132 -0
  23. package/cpp/utils/NitroDefines.hpp +1 -8
  24. package/cpp/utils/NitroTypeInfo.cpp +80 -0
  25. package/cpp/utils/NitroTypeInfo.hpp +8 -65
  26. package/cpp/utils/WeakReference.hpp +6 -7
  27. package/cpp/views/CachedProp.hpp +2 -1
  28. package/ios/turbomodule/NativeNitroModules+NewArch.mm +1 -1
  29. package/ios/utils/RuntimeError.hpp +1 -1
  30. package/lib/commonjs/Sync.js +2 -0
  31. package/lib/commonjs/Sync.js.map +1 -0
  32. package/lib/commonjs/index.js +22 -0
  33. package/lib/commonjs/index.js.map +1 -1
  34. package/lib/commonjs/views/HybridView.js +0 -11
  35. package/lib/commonjs/views/HybridView.js.map +1 -1
  36. package/lib/commonjs/views/getHostComponent.js +20 -1
  37. package/lib/commonjs/views/getHostComponent.js.map +1 -1
  38. package/lib/module/Sync.js +2 -0
  39. package/lib/module/Sync.js.map +1 -0
  40. package/lib/module/index.js +2 -0
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/views/HybridView.js +0 -26
  43. package/lib/module/views/HybridView.js.map +1 -1
  44. package/lib/module/views/getHostComponent.js +21 -1
  45. package/lib/module/views/getHostComponent.js.map +1 -1
  46. package/lib/tsconfig.build.tsbuildinfo +1 -1
  47. package/lib/typescript/Sync.d.ts +11 -0
  48. package/lib/typescript/Sync.d.ts.map +1 -0
  49. package/lib/typescript/index.d.ts +2 -0
  50. package/lib/typescript/index.d.ts.map +1 -1
  51. package/lib/typescript/views/HybridView.d.ts +89 -16
  52. package/lib/typescript/views/HybridView.d.ts.map +1 -1
  53. package/lib/typescript/views/getHostComponent.d.ts +46 -3
  54. package/lib/typescript/views/getHostComponent.d.ts.map +1 -1
  55. package/package.json +4 -4
  56. package/src/Sync.ts +8 -0
  57. package/src/index.ts +3 -0
  58. package/src/views/HybridView.ts +96 -18
  59. package/src/views/getHostComponent.ts +67 -5
  60. package/cpp/utils/OwningLock.hpp +0 -56
@@ -41,6 +41,7 @@ Pod::Spec.new do |s|
41
41
  "cpp/jsi/JSIConverter.hpp",
42
42
  "cpp/platform/NitroLogger.hpp",
43
43
  "cpp/threading/Dispatcher.hpp",
44
+ "cpp/utils/JSCallback.hpp",
44
45
  "cpp/utils/NitroHash.hpp",
45
46
  "cpp/utils/NitroDefines.hpp",
46
47
  "cpp/views/CachedProp.hpp",
package/README.md CHANGED
@@ -181,6 +181,12 @@ The following C++ / JS types are supported out of the box:
181
181
  <td><code>(T...) -&gt; <a href="./ios/core/Promise.swift">Promise&lt;T&gt;</a></code></td>
182
182
  <td><code>(T...) -&gt; <a href="./android/src/main/java/com/margelo/nitro/core/Promise.kt">Promise&lt;T&gt;</a></code></td>
183
183
  </tr>
184
+ <tr>
185
+ <td><code>Sync&lt;(T...) =&gt; R&gt;</code></td>
186
+ <td><code>std::function&lt;R (T...)&gt;</code></td>
187
+ <td><code>@escaping (T...) -&gt; R</code></td>
188
+ <td><code>(T...) -&gt; R</code></td>
189
+ </tr>
184
190
  <tr>
185
191
  <td><code>Error</code></td>
186
192
  <td><code>std::exception_ptr</code></td>
@@ -7,7 +7,7 @@ buildscript {
7
7
  }
8
8
 
9
9
  dependencies {
10
- classpath "com.android.tools.build:gradle:8.8.0"
10
+ classpath "com.android.tools.build:gradle:8.8.1"
11
11
  }
12
12
  }
13
13
 
@@ -1,5 +1,5 @@
1
1
  Nitro_kotlinVersion=2.0.21
2
2
  Nitro_minSdkVersion=23
3
- Nitro_targetSdkVersion=34
3
+ Nitro_targetSdkVersion=35
4
4
  Nitro_compileSdkVersion=34
5
5
  Nitro_ndkVersion=27.1.12297006
@@ -59,7 +59,7 @@ public:
59
59
  auto instance = _javaClass->newObject(_defaultConstructor);
60
60
  #ifdef NITRO_DEBUG
61
61
  if (instance == nullptr) [[unlikely]] {
62
- throw std::runtime_error("Failed to create an instance of \"JHybridTestObjectSwiftKotlinSpec\" - the constructor returned null!");
62
+ throw std::runtime_error("Failed to create an instance of \"" + _javaClass->toString() + "\" - the constructor returned null!");
63
63
  }
64
64
  #endif
65
65
  return instance;
@@ -20,4 +20,15 @@ abstract class HybridView: HybridObject() {
20
20
  @get:DoNotStrip
21
21
  @get:Keep
22
22
  abstract val view: View
23
+
24
+ /**
25
+ * Called right before updating props.
26
+ * React props are updated in a single batch/transaction.
27
+ */
28
+ open fun beforeUpdate() { /* noop */ }
29
+ /**
30
+ * Called right after updating props.
31
+ * React props are updated in a single batch/transaction.
32
+ */
33
+ open fun afterUpdate() { /* noop */ }
23
34
  }
@@ -61,7 +61,7 @@ bool NativeArrayBuffer::isOwner() const noexcept {
61
61
 
62
62
  // 3. JSArrayBuffer
63
63
 
64
- JSArrayBuffer::JSArrayBuffer(jsi::Runtime* runtime, BorrowingReference<jsi::ArrayBuffer> jsReference)
64
+ JSArrayBuffer::JSArrayBuffer(jsi::Runtime& runtime, BorrowingReference<jsi::ArrayBuffer> jsReference)
65
65
  : ArrayBuffer(), _runtime(runtime), _jsReference(jsReference), _initialThreadId(std::this_thread::get_id()) {}
66
66
 
67
67
  JSArrayBuffer::~JSArrayBuffer() {}
@@ -72,13 +72,12 @@ uint8_t* JSArrayBuffer::data() {
72
72
  "If you want to access it elsewhere, copy it first.");
73
73
  }
74
74
 
75
- OwningLock<jsi::ArrayBuffer> lock = _jsReference.lock();
76
75
  if (!_jsReference) [[unlikely]] {
77
76
  // JS Part has been deleted - data is now nullptr.
78
77
  return nullptr;
79
78
  }
80
79
  // JS Part is still alive - we can assume that the jsi::Runtime is safe to access here too.
81
- return _jsReference->data(*_runtime);
80
+ return _jsReference->data(_runtime);
82
81
  }
83
82
 
84
83
  size_t JSArrayBuffer::size() const {
@@ -87,13 +86,12 @@ size_t JSArrayBuffer::size() const {
87
86
  "If you want to access it elsewhere, copy it first.");
88
87
  }
89
88
 
90
- OwningLock<jsi::ArrayBuffer> lock = _jsReference.lock();
91
89
  if (!_jsReference) [[unlikely]] {
92
90
  // JS Part has been deleted - size is now 0.
93
91
  return 0;
94
92
  }
95
93
  // JS Part is still alive - we can assume that the jsi::Runtime is safe to access here too.
96
- return _jsReference->size(*_runtime);
94
+ return _jsReference->size(_runtime);
97
95
  }
98
96
 
99
97
  bool JSArrayBuffer::isOwner() const noexcept {
@@ -122,7 +122,7 @@ private:
122
122
  */
123
123
  class JSArrayBuffer final : public ArrayBuffer {
124
124
  public:
125
- explicit JSArrayBuffer(jsi::Runtime* runtime, BorrowingReference<jsi::ArrayBuffer> jsReference);
125
+ explicit JSArrayBuffer(jsi::Runtime& runtime, BorrowingReference<jsi::ArrayBuffer> jsReference);
126
126
  ~JSArrayBuffer();
127
127
 
128
128
  public:
@@ -140,7 +140,7 @@ public:
140
140
  bool isOwner() const noexcept override;
141
141
 
142
142
  private:
143
- jsi::Runtime* _runtime;
143
+ jsi::Runtime& _runtime;
144
144
  BorrowingReference<jsi::ArrayBuffer> _jsReference;
145
145
  std::thread::id _initialThreadId;
146
146
  };
@@ -57,7 +57,8 @@ public:
57
57
  public:
58
58
  // functions
59
59
  inline jsi::Function toJSFunction(jsi::Runtime& runtime) const {
60
- return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, _name), _paramCount, _function);
60
+ return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, _name), static_cast<unsigned int>(_paramCount),
61
+ _function);
61
62
  }
62
63
 
63
64
  private:
@@ -109,12 +110,15 @@ public:
109
110
  std::string message = exception.what();
110
111
  throw jsi::JSError(runtime, funcName + ": " + message);
111
112
  #ifdef ANDROID
113
+ #pragma clang diagnostic push
114
+ #pragma clang diagnostic ignored "-Wexceptions"
112
115
  // Workaround for https://github.com/mrousavy/nitro/issues/382
113
116
  } catch (const std::runtime_error& exception) {
114
117
  // Some exception was thrown - add method name information and re-throw as `JSError`.
115
118
  std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
116
119
  std::string message = exception.what();
117
120
  throw jsi::JSError(runtime, funcName + ": " + message);
121
+ #pragma clang diagnostic pop
118
122
  #endif
119
123
  } catch (...) {
120
124
  // Some unknown exception was thrown - add method name information and re-throw as `JSError`.
@@ -46,7 +46,9 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
46
46
  auto cachedObject = _objectCache.find(&runtime);
47
47
  if (cachedObject != _objectCache.end()) {
48
48
  // 1.1. We have a WeakObject, try to see if it is still alive
49
- OwningLock<jsi::WeakObject> lock = cachedObject->second.lock();
49
+ if (cachedObject->second == nullptr) [[unlikely]] {
50
+ throw std::runtime_error("HybridObject \"" + getName() + "\" was cached, but the reference got destroyed!");
51
+ }
50
52
  jsi::Value value = cachedObject->second->lock(runtime);
51
53
  if (!value.isUndefined()) {
52
54
  // 1.2. It is still alive - we can use it instead of creating a new one! But first, let's update memory-size
@@ -30,7 +30,7 @@ public:
30
30
  Promise(const Promise&) = delete;
31
31
 
32
32
  private:
33
- Promise() {}
33
+ Promise() = default;
34
34
 
35
35
  public:
36
36
  ~Promise() {
@@ -122,7 +122,8 @@ public:
122
122
  */
123
123
  void reject(const std::exception_ptr& exception) {
124
124
  if (exception == nullptr) [[unlikely]] {
125
- throw std::runtime_error("Cannot reject Promise with a null exception_ptr!");
125
+ std::string typeName = TypeInfo::getFriendlyTypename<TResult>(true);
126
+ throw std::runtime_error("Cannot reject Promise<" + typeName + "> with a null exception_ptr!");
126
127
  }
127
128
 
128
129
  std::unique_lock lock(_mutex);
@@ -208,7 +209,8 @@ public:
208
209
  */
209
210
  inline const TResult& getResult() {
210
211
  if (!isResolved()) {
211
- throw std::runtime_error("Cannot get result when Promise is not yet resolved!");
212
+ std::string typeName = TypeInfo::getFriendlyTypename<TResult>(true);
213
+ throw std::runtime_error("Cannot get result when Promise<" + typeName + "> is not yet resolved!");
212
214
  }
213
215
  return std::get<TResult>(_state);
214
216
  }
@@ -218,7 +220,8 @@ public:
218
220
  */
219
221
  inline const std::exception_ptr& getError() {
220
222
  if (!isRejected()) {
221
- throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
223
+ std::string typeName = TypeInfo::getFriendlyTypename<TResult>(true);
224
+ throw std::runtime_error("Cannot get error when Promise<" + typeName + "> is not yet rejected!");
222
225
  }
223
226
  return std::get<std::exception_ptr>(_state);
224
227
  }
@@ -264,13 +267,13 @@ public:
264
267
  Promise(const Promise&) = delete;
265
268
 
266
269
  private:
267
- Promise() {}
270
+ Promise() = default;
268
271
 
269
272
  public:
270
273
  ~Promise() {
271
274
  if (isPending()) [[unlikely]] {
272
275
  std::runtime_error error("Timeouted: Promise<void> was destroyed!");
273
- reject(std::make_exception_ptr(std::move(error)));
276
+ reject(std::make_exception_ptr(error));
274
277
  }
275
278
  }
276
279
 
@@ -323,7 +326,7 @@ public:
323
326
  }
324
327
  void reject(const std::exception_ptr& exception) {
325
328
  if (exception == nullptr) [[unlikely]] {
326
- throw std::runtime_error("Cannot reject Promise with a null exception_ptr!");
329
+ throw std::runtime_error("Cannot reject Promise<void> with a null exception_ptr!");
327
330
  }
328
331
 
329
332
  std::unique_lock lock(_mutex);
@@ -383,7 +386,7 @@ public:
383
386
  public:
384
387
  inline const std::exception_ptr& getError() {
385
388
  if (!isRejected()) {
386
- throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
389
+ throw std::runtime_error("Cannot get error when Promise<void> is not yet rejected!");
387
390
  }
388
391
  return _error;
389
392
  }
@@ -29,9 +29,9 @@ class JSICacheReference;
29
29
  * `jsi::Value`s are managed by a `jsi::Runtime`, and will be deleted if the `jsi::Runtime`
30
30
  * is deleted - even if there are still strong references to the `jsi::Value`.
31
31
  *
32
- * To access a `BorrowingReference<jsi::Value>` safely, use `lock()` to get an `OwningLock<jsi::Value>`.
33
- * This will allow you to access the `jsi::Value` as long as the `OwningLock` is alive,
34
- * and `JSICache` will hold any garbage collection calls until the `OwningLock` is destroyed.
32
+ * To access a `BorrowingReference<jsi::Value>` safely, make sure you are using it from
33
+ * the same Thread that it was created on. This ensures that the `jsi::Runtime` cannot
34
+ * delete it while you are still using it.
35
35
  */
36
36
  class JSICache final : public jsi::NativeState {
37
37
  public:
@@ -51,7 +51,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer
51
51
  JSICacheReference cache = JSICache::getOrCreateCache(runtime);
52
52
  auto borrowingArrayBuffer = cache.makeShared(object.getArrayBuffer(runtime));
53
53
 
54
- return std::make_shared<JSArrayBuffer>(&runtime, borrowingArrayBuffer);
54
+ return std::make_shared<JSArrayBuffer>(runtime, borrowingArrayBuffer);
55
55
  }
56
56
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<jsi::MutableBuffer>& buffer) {
57
57
  return jsi::ArrayBuffer(runtime, buffer);
@@ -31,7 +31,7 @@ struct JSIConverter<std::exception_ptr> final {
31
31
  }
32
32
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::exception_ptr& exception) {
33
33
  if (exception == nullptr) [[unlikely]] {
34
- throw std::runtime_error("Cannot convert an empty exception_ptr to a JS Error!");
34
+ throw std::runtime_error("Cannot convert a nullptr exception_ptr to a JS Error!");
35
35
  }
36
36
 
37
37
  try {
@@ -40,10 +40,13 @@ struct JSIConverter<std::exception_ptr> final {
40
40
  jsi::JSError error(runtime, e.what());
41
41
  return jsi::Value(runtime, error.value());
42
42
  #ifdef ANDROID
43
+ #pragma clang diagnostic push
44
+ #pragma clang diagnostic ignored "-Wexceptions"
43
45
  // Workaround for https://github.com/mrousavy/nitro/issues/382
44
46
  } catch (const std::runtime_error& e) {
45
47
  jsi::JSError error(runtime, e.what());
46
48
  return jsi::Value(runtime, error.value());
49
+ #pragma clang diagnostic pop
47
50
  #endif
48
51
  } catch (...) {
49
52
  // Some unknown exception was thrown - maybe an Objective-C error?
@@ -12,12 +12,10 @@ template <typename T, typename Enable>
12
12
  struct JSIConverter;
13
13
  } // namespace margelo::nitro
14
14
 
15
- #include "JSIConverter.hpp"
16
-
17
15
  #include "Dispatcher.hpp"
16
+ #include "JSCallback.hpp"
18
17
  #include "JSICache.hpp"
19
18
  #include "PromiseType.hpp"
20
- #include <functional>
21
19
  #include <jsi/jsi.h>
22
20
 
23
21
  namespace margelo::nitro {
@@ -25,49 +23,31 @@ namespace margelo::nitro {
25
23
  using namespace facebook;
26
24
 
27
25
  // [](Args...) -> T {} <> (Args...) => T
28
- template <typename ReturnType, typename... Args>
29
- struct JSIConverter<std::function<ReturnType(Args...)>> final {
26
+ template <typename R, typename... Args>
27
+ struct JSIConverter<std::function<R(Args...)>> final {
28
+ // Use AsyncJSCallback or SyncJSCallback
29
+ inline static constexpr bool isAsync = is_promise_v<R> || std::is_void_v<R>;
30
30
  // Promise<T> -> T
31
- using ResultingType = promise_type_v<ReturnType>;
31
+ using ActualR = std::conditional_t<isAsync, promise_type_v<R>, R>;
32
32
 
33
- static inline std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
33
+ static inline std::function<R(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
34
34
  // Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
35
35
  auto cache = JSICache::getOrCreateCache(runtime);
36
36
  jsi::Function function = arg.asObject(runtime).asFunction(runtime);
37
37
  BorrowingReference<jsi::Function> sharedFunction = cache.makeShared(std::move(function));
38
+ SyncJSCallback<ActualR(Args...)> callback(runtime, std::move(sharedFunction));
38
39
 
39
- std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
40
- std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
41
-
42
- // Create a C++ function that can be called by the consumer.
43
- // This will call the jsi::Function if it is still alive.
44
- return [&runtime, weakDispatcher = std::move(weakDispatcher), sharedFunction = std::move(sharedFunction)](Args... args) -> ReturnType {
45
- // Try to get the JS Dispatcher if the Runtime is still alive
46
- std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
47
- if (!dispatcher) {
48
- if constexpr (std::is_void_v<ResultingType>) {
49
- Logger::log(LogLevel::Error, "JSIConverter",
50
- "Tried calling void(..) function, but the JS Dispatcher has already been deleted by JS!");
51
- return;
52
- } else {
53
- throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
54
- }
55
- }
56
-
57
- if constexpr (std::is_void_v<ResultingType>) {
58
- dispatcher->runAsync([&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() {
59
- callJSFunction(runtime, sharedFunction, args...);
60
- });
61
- } else {
62
- return dispatcher->runAsyncAwaitable<ResultingType>(
63
- [&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() -> ResultingType {
64
- return callJSFunction(runtime, sharedFunction, args...);
65
- });
66
- }
67
- };
40
+ if constexpr (isAsync) {
41
+ // Return type is `Promise<T>` or `void` - it's an async callback!
42
+ std::shared_ptr<Dispatcher> dispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
43
+ return AsyncJSCallback<ActualR(Args...)>(std::move(callback), dispatcher);
44
+ } else {
45
+ // Return type is `T` - it's a sync callback!
46
+ return callback;
47
+ }
68
48
  }
69
49
 
70
- static inline jsi::Value toJSI(jsi::Runtime& runtime, std::function<ReturnType(Args...)>&& function) {
50
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::function<R(Args...)>&& function) {
71
51
  jsi::HostFunctionType jsFunction = [function = std::move(function)](jsi::Runtime& runtime, const jsi::Value&, const jsi::Value* args,
72
52
  size_t count) -> jsi::Value {
73
53
  if (count != sizeof...(Args)) [[unlikely]] {
@@ -80,8 +60,8 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
80
60
  std::move(jsFunction));
81
61
  }
82
62
 
83
- static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::function<ReturnType(Args...)>& function) {
84
- std::function<ReturnType(Args...)> copy = function;
63
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::function<R(Args...)>& function) {
64
+ std::function<R(Args...)> copy = function;
85
65
  return toJSI(runtime, std::move(copy));
86
66
  }
87
67
 
@@ -94,40 +74,17 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
94
74
  }
95
75
 
96
76
  private:
97
- static inline ResultingType callJSFunction(jsi::Runtime& runtime, const BorrowingReference<jsi::Function>& function,
98
- const Args&... args) {
99
- if (!function) {
100
- if constexpr (std::is_void_v<ResultingType>) {
101
- // runtime has already been deleted. since this returns void, we can just ignore it being deleted.
102
- Logger::log(LogLevel::Error, "JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
103
- return;
104
- } else {
105
- // runtime has already been deleted, but we are expecting a return value - throw an error in this case.
106
- throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
107
- }
108
- }
109
-
110
- if constexpr (std::is_void_v<ResultingType>) {
111
- // It returns void. Just call the function
112
- function->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
113
- } else {
114
- // It returns some kind of value - call the function, and convert the return value.
115
- jsi::Value result = function->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
116
- return JSIConverter<ResultingType>::fromJSI(runtime, std::move(result));
117
- }
118
- }
119
-
120
77
  template <size_t... Is>
121
- static inline jsi::Value callHybridFunction(const std::function<ReturnType(Args...)>& function, jsi::Runtime& runtime,
122
- const jsi::Value* args, std::index_sequence<Is...>) {
123
- if constexpr (std::is_void_v<ReturnType>) {
78
+ static inline jsi::Value callHybridFunction(const std::function<R(Args...)>& function, jsi::Runtime& runtime, const jsi::Value* args,
79
+ std::index_sequence<Is...>) {
80
+ if constexpr (std::is_void_v<R>) {
124
81
  // it is a void function (will return undefined in JS)
125
82
  function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
126
83
  return jsi::Value::undefined();
127
84
  } else {
128
85
  // it is a custom type, parse it to a JS value
129
- ReturnType result = function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
130
- return JSIConverter<ReturnType>::toJSI(runtime, std::forward<ReturnType>(result));
86
+ R result = function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
87
+ return JSIConverter<R>::toJSI(runtime, std::forward<R>(result));
131
88
  }
132
89
  }
133
90
  };
@@ -12,7 +12,7 @@ struct JSIConverter;
12
12
  } // namespace margelo::nitro
13
13
 
14
14
  #include "JSIConverter.hpp"
15
-
15
+ #include "NitroTypeInfo.hpp"
16
16
  #include "Promise.hpp"
17
17
  #include <exception>
18
18
  #include <jsi/jsi.h>
@@ -92,7 +92,8 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
92
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
- throw std::runtime_error("Promise has invalid state!");
95
+ std::string typeName = TypeInfo::getFriendlyTypename<TResult>(true);
96
+ throw std::runtime_error("Promise<" + typeName + "> has invalid state!");
96
97
  }
97
98
  }
98
99
 
@@ -27,7 +27,10 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
27
27
  auto cachedPrototype = prototypeCache.find(prototype->getNativeInstanceId());
28
28
  if (cachedPrototype != prototypeCache.end()) {
29
29
  const BorrowingReference<jsi::Object>& cachedObject = cachedPrototype->second;
30
- return jsi::Value(runtime, *cachedObject).getObject(runtime);
30
+ if (cachedObject != nullptr) {
31
+ // 1.1. Found it in cache! Copy & return it.
32
+ return jsi::Value(runtime, *cachedObject);
33
+ }
31
34
  }
32
35
 
33
36
  // 2. We didn't find the given prototype in cache (either it's a new prototype, or a new runtime),
@@ -66,19 +69,19 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
66
69
  /* descriptorObj */ property);
67
70
  }
68
71
 
69
- // 6. Throw it into our cache so the next lookup can be cached and therefore faster
70
- JSICacheReference jsiCache = JSICache::getOrCreateCache(runtime);
71
- BorrowingReference<jsi::Object> cachedObject = jsiCache.makeShared(std::move(object));
72
- prototypeCache.emplace(prototype->getNativeInstanceId(), cachedObject);
73
-
74
- // 7. In DEBUG, add a __type info to the prototype object.
72
+ // 6. In DEBUG, add a __type info to the prototype object.
75
73
  #ifdef NITRO_DEBUG
76
74
  auto prototypeName = "Prototype<" + typeName + ">";
77
- cachedObject->setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, prototypeName));
75
+ object.setProperty(runtime, "__type", jsi::String::createFromUtf8(runtime, prototypeName));
78
76
  #endif
79
77
 
78
+ // 7. Throw it into our cache so the next lookup can be cached and therefore faster
79
+ JSICacheReference jsiCache = JSICache::getOrCreateCache(runtime);
80
+ BorrowingReference<jsi::Object> sharedObject = jsiCache.makeShared(std::move(object));
81
+ prototypeCache.emplace(prototype->getNativeInstanceId(), sharedObject);
82
+
80
83
  // 8. Return it!
81
- return jsi::Value(runtime, *cachedObject);
84
+ return jsi::Value(runtime, *sharedObject);
82
85
  }
83
86
 
84
87
  jsi::Value HybridObjectPrototype::getPrototype(jsi::Runtime& runtime) {
@@ -32,6 +32,9 @@ std::vector<std::string> HybridObjectRegistry::getAllHybridObjectNames() {
32
32
 
33
33
  std::string HybridObjectRegistry::getAllRegisteredHybridObjectNamesToString() {
34
34
  std::vector<std::string> names = getAllHybridObjectNames();
35
+ if (names.empty()) {
36
+ return "";
37
+ }
35
38
  return std::accumulate(std::next(names.begin()), names.end(), names[0], [](std::string a, std::string b) { return a + ", " + b; });
36
39
  }
37
40
 
@@ -16,17 +16,29 @@ namespace margelo::nitro {
16
16
  template <typename T>
17
17
  struct promise_type {
18
18
  using type = void;
19
+ using is_promise = std::false_type;
20
+ };
21
+
22
+ template <>
23
+ struct promise_type<void> {
24
+ using type = void;
25
+ using is_promise = std::true_type;
19
26
  };
20
27
  template <typename T>
21
28
  struct promise_type<Promise<T>> {
22
29
  using type = T;
30
+ using is_promise = std::true_type;
23
31
  };
24
32
  template <typename T>
25
33
  struct promise_type<std::shared_ptr<Promise<T>>> {
26
34
  using type = T;
35
+ using is_promise = std::true_type;
27
36
  };
28
37
 
29
38
  template <typename T>
30
39
  using promise_type_v = typename promise_type<std::remove_reference_t<T>>::type;
31
40
 
41
+ template <typename T>
42
+ inline constexpr bool is_promise_v = promise_type<std::remove_reference_t<T>>::is_promise::value;
43
+
32
44
  } // namespace margelo::nitro
@@ -45,18 +45,20 @@ std::shared_ptr<Dispatcher> Dispatcher::getRuntimeGlobalDispatcher(jsi::Runtime&
45
45
  getRuntimeId(runtime).c_str());
46
46
  jsi::Value dispatcherHolderValue = getRuntimeGlobalDispatcherHolder(runtime);
47
47
  jsi::Object dispatcherHolder = dispatcherHolderValue.getObject(runtime);
48
- return dispatcherHolder.getNativeState<Dispatcher>(runtime);
48
+ std::shared_ptr<Dispatcher> dispatcher = dispatcherHolder.getNativeState<Dispatcher>(runtime);
49
+ _globalCache[&runtime] = dispatcher;
50
+ return dispatcher;
49
51
  }
50
52
 
51
53
  jsi::Value Dispatcher::getRuntimeGlobalDispatcherHolder(jsi::Runtime& runtime) {
52
54
  #ifdef NITRO_DEBUG
53
55
  if (!runtime.global().hasProperty(runtime, GLOBAL_DISPATCHER_HOLDER_NAME)) [[unlikely]] {
54
56
  throw std::runtime_error("Failed to get current Dispatcher - the global Dispatcher "
55
- "holder (global." +
57
+ "holder (`global." +
56
58
  std::string(GLOBAL_DISPATCHER_HOLDER_NAME) +
57
- ") "
58
- "does not exist! Was Dispatcher::installDispatcherIntoRuntime() called "
59
- "for this jsi::Runtime?");
59
+ "`) "
60
+ "does not exist! Was `Dispatcher::installDispatcherIntoRuntime()` called "
61
+ "for this `jsi::Runtime`?");
60
62
  }
61
63
  #endif
62
64
  return runtime.global().getProperty(runtime, GLOBAL_DISPATCHER_HOLDER_NAME);