react-native-nitro-modules 0.0.2 → 0.0.4

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 (120) hide show
  1. package/NitroModules.podspec +49 -0
  2. package/android/CMakeLists.txt +44 -7
  3. package/android/build.gradle +28 -24
  4. package/cpp/core/AnyMap.cpp +181 -0
  5. package/cpp/core/AnyMap.hpp +191 -0
  6. package/cpp/core/HybridContext.hpp +51 -0
  7. package/cpp/core/HybridObject.cpp +220 -0
  8. package/cpp/core/HybridObject.hpp +241 -0
  9. package/cpp/core/PointerHolder.hpp +93 -0
  10. package/cpp/jsi/ArrayBuffer.hpp +79 -0
  11. package/cpp/jsi/JSICache.hpp +145 -0
  12. package/cpp/jsi/JSIConverter.hpp +610 -0
  13. package/cpp/jsi/Promise.cpp +54 -0
  14. package/cpp/jsi/Promise.hpp +54 -0
  15. package/cpp/platform/ThreadUtils.hpp +23 -0
  16. package/cpp/registry/HybridObjectRegistry.cpp +57 -0
  17. package/cpp/registry/HybridObjectRegistry.hpp +44 -0
  18. package/cpp/test-object/TestHybridObject.cpp +37 -0
  19. package/cpp/test-object/TestHybridObject.hpp +87 -0
  20. package/cpp/threading/CallInvokerDispatcher.hpp +33 -0
  21. package/cpp/threading/Dispatcher.cpp +56 -0
  22. package/cpp/threading/Dispatcher.hpp +82 -0
  23. package/cpp/turbomodule/NativeNitroModules.cpp +70 -0
  24. package/cpp/turbomodule/NativeNitroModules.h +7 -0
  25. package/cpp/turbomodule/NativeNitroModules.hpp +35 -0
  26. package/cpp/turbomodule/RegisterNativeNitroModules.cpp +33 -0
  27. package/cpp/turbomodule/RegisterNativeNitroModules.hpp +21 -0
  28. package/cpp/utils/BorrowingReference+Owning.hpp +34 -0
  29. package/cpp/utils/BorrowingReference.hpp +115 -0
  30. package/cpp/utils/DoesClassExist.hpp +23 -0
  31. package/cpp/utils/GetRuntimeID.hpp +28 -0
  32. package/cpp/utils/NitroDefines.hpp +32 -0
  33. package/cpp/utils/NitroHash.hpp +42 -0
  34. package/cpp/utils/NitroLogger.hpp +55 -0
  35. package/cpp/utils/OwningLock.hpp +54 -0
  36. package/cpp/utils/OwningReference.hpp +214 -0
  37. package/cpp/utils/TypeInfo.hpp +81 -0
  38. package/ios/core/HybridObjectSpec.swift +52 -0
  39. package/ios/core/RuntimeError.swift +17 -0
  40. package/ios/platform/ThreadUtils.cpp +28 -0
  41. package/ios/turbomodule/NitroModuleOnLoad.mm +31 -0
  42. package/lib/AnyMap.d.ts +16 -0
  43. package/lib/AnyMap.js +1 -0
  44. package/lib/HybridObject.d.ts +57 -0
  45. package/lib/HybridObject.js +1 -0
  46. package/lib/ModuleNotFoundError.d.ts +6 -0
  47. package/lib/ModuleNotFoundError.js +61 -0
  48. package/lib/NativeNitro.d.ts +8 -0
  49. package/lib/NativeNitro.js +3 -0
  50. package/lib/NativeNitroModules.d.ts +7 -0
  51. package/lib/NativeNitroModules.js +17 -0
  52. package/lib/NitroModules.d.ts +17 -0
  53. package/lib/NitroModules.js +21 -0
  54. package/lib/__tests__/index.test.d.ts +0 -0
  55. package/lib/__tests__/index.test.js +2 -0
  56. package/lib/commonjs/AnyMap.js +2 -0
  57. package/lib/commonjs/AnyMap.js.map +1 -0
  58. package/lib/commonjs/HybridObject.js +2 -0
  59. package/lib/commonjs/HybridObject.js.map +1 -0
  60. package/lib/commonjs/ModuleNotFoundError.js +72 -0
  61. package/lib/commonjs/ModuleNotFoundError.js.map +1 -0
  62. package/lib/commonjs/NativeNitroModules.js +24 -0
  63. package/lib/commonjs/NativeNitroModules.js.map +1 -0
  64. package/lib/commonjs/NitroModules.js +32 -0
  65. package/lib/commonjs/NitroModules.js.map +1 -0
  66. package/lib/commonjs/createTestObject.js +15 -0
  67. package/lib/commonjs/createTestObject.js.map +1 -0
  68. package/lib/commonjs/index.js +44 -5
  69. package/lib/commonjs/index.js.map +1 -1
  70. package/lib/createTestObject.d.ts +22 -0
  71. package/lib/createTestObject.js +7 -0
  72. package/lib/index.d.ts +4 -0
  73. package/lib/index.js +4 -0
  74. package/lib/module/AnyMap.js +2 -0
  75. package/lib/module/AnyMap.js.map +1 -0
  76. package/lib/module/HybridObject.js +2 -0
  77. package/lib/module/HybridObject.js.map +1 -0
  78. package/lib/module/ModuleNotFoundError.js +65 -0
  79. package/lib/module/ModuleNotFoundError.js.map +1 -0
  80. package/lib/module/NativeNitroModules.js +18 -0
  81. package/lib/module/NativeNitroModules.js.map +1 -0
  82. package/lib/module/NitroModules.js +27 -0
  83. package/lib/module/NitroModules.js.map +1 -0
  84. package/lib/module/createTestObject.js +8 -0
  85. package/lib/module/createTestObject.js.map +1 -0
  86. package/lib/module/index.js +4 -4
  87. package/lib/module/index.js.map +1 -1
  88. package/lib/tsconfig.tsbuildinfo +1 -0
  89. package/package.json +76 -49
  90. package/react-native.config.js +16 -0
  91. package/src/AnyMap.ts +22 -0
  92. package/src/HybridObject.ts +58 -0
  93. package/src/ModuleNotFoundError.ts +90 -0
  94. package/src/NativeNitroModules.ts +26 -0
  95. package/src/NitroModules.ts +30 -0
  96. package/src/__tests__/index.test.tsx +1 -0
  97. package/src/createTestObject.ts +40 -0
  98. package/src/index.ts +4 -0
  99. package/LICENSE +0 -20
  100. package/README.md +0 -32
  101. package/android/cpp-adapter.cpp +0 -8
  102. package/android/src/main/AndroidManifest.xml +0 -3
  103. package/android/src/main/AndroidManifestNew.xml +0 -2
  104. package/android/src/main/java/com/nitro/NitroModule.java +0 -34
  105. package/android/src/main/java/com/nitro/NitroPackage.java +0 -44
  106. package/cpp/react-native-nitro.cpp +0 -7
  107. package/cpp/react-native-nitro.h +0 -8
  108. package/ios/Nitro.h +0 -15
  109. package/ios/Nitro.mm +0 -21
  110. package/lib/commonjs/NativeNitro.js +0 -9
  111. package/lib/commonjs/NativeNitro.js.map +0 -1
  112. package/lib/module/NativeNitro.js +0 -3
  113. package/lib/module/NativeNitro.js.map +0 -1
  114. package/lib/typescript/src/NativeNitro.d.ts +0 -7
  115. package/lib/typescript/src/NativeNitro.d.ts.map +0 -1
  116. package/lib/typescript/src/index.d.ts +0 -2
  117. package/lib/typescript/src/index.d.ts.map +0 -1
  118. package/react-native-nitro.podspec +0 -41
  119. package/src/NativeNitro.ts +0 -8
  120. package/src/index.tsx +0 -5
@@ -0,0 +1,610 @@
1
+ //
2
+ // Created by Marc Rousavy on 21.02.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ namespace margelo::nitro {
8
+ class HybridObject;
9
+ }
10
+
11
+ #include "AnyMap.hpp"
12
+ #include "ArrayBuffer.hpp"
13
+ #include "Dispatcher.hpp"
14
+ #include "HybridObject.hpp"
15
+ #include "JSICache.hpp"
16
+ #include "NitroHash.hpp"
17
+ #include "Promise.hpp"
18
+ #include "TypeInfo.hpp"
19
+ #include <array>
20
+ #include <future>
21
+ #include <jsi/jsi.h>
22
+ #include <memory>
23
+ #include <tuple>
24
+ #include <type_traits>
25
+ #include <unordered_map>
26
+ #include <variant>
27
+
28
+ #define DO_NULL_CHECKS true
29
+
30
+ namespace margelo::nitro {
31
+
32
+ /**
33
+ The JSIConverter<T> class can convert any type from and to a jsi::Value.
34
+ It uses templates to statically create fromJSI/toJSI methods, and will throw compile-time errors
35
+ if a given type is not convertable.
36
+ Value types, custom types (HostObjects), and even functions with any number of arguments/types are supported.
37
+ This type can be extended by just creating a new template for JSIConverter in a header.
38
+ */
39
+
40
+ using namespace facebook;
41
+
42
+ // Unknown type (error)
43
+ template <typename ArgType, typename Enable = void> struct JSIConverter final {
44
+ JSIConverter() = delete;
45
+
46
+ static inline ArgType fromJSI(jsi::Runtime&, const jsi::Value&) {
47
+ static_assert(always_false<ArgType>::value, "This type is not supported by the JSIConverter!");
48
+ return ArgType();
49
+ }
50
+ static inline jsi::Value toJSI(jsi::Runtime&, ArgType) {
51
+ static_assert(always_false<ArgType>::value, "This type is not supported by the JSIConverter!");
52
+ return jsi::Value::undefined();
53
+ }
54
+
55
+ private:
56
+ template <typename> struct always_false : std::false_type {};
57
+ };
58
+
59
+ // int <> number
60
+ template <> struct JSIConverter<int> {
61
+ static inline int fromJSI(jsi::Runtime&, const jsi::Value& arg) {
62
+ return static_cast<int>(arg.asNumber());
63
+ }
64
+ static inline jsi::Value toJSI(jsi::Runtime&, int arg) {
65
+ return jsi::Value(arg);
66
+ }
67
+ };
68
+
69
+ // std::monostate <> null
70
+ template <> struct JSIConverter<std::monostate> {
71
+ static inline std::monostate fromJSI(jsi::Runtime&, const jsi::Value& arg) {
72
+ return std::monostate();
73
+ }
74
+ static inline jsi::Value toJSI(jsi::Runtime&, std::monostate arg) {
75
+ return jsi::Value::null();
76
+ }
77
+ };
78
+
79
+ // double <> number
80
+ template <> struct JSIConverter<double> {
81
+ static inline double fromJSI(jsi::Runtime&, const jsi::Value& arg) {
82
+ return arg.asNumber();
83
+ }
84
+ static inline jsi::Value toJSI(jsi::Runtime&, double arg) {
85
+ return jsi::Value(arg);
86
+ }
87
+ };
88
+
89
+ // float <> number
90
+ template <> struct JSIConverter<float> {
91
+ static inline float fromJSI(jsi::Runtime&, const jsi::Value& arg) {
92
+ return static_cast<float>(arg.asNumber());
93
+ }
94
+ static inline jsi::Value toJSI(jsi::Runtime&, float arg) {
95
+ return jsi::Value(static_cast<double>(arg));
96
+ }
97
+ };
98
+
99
+ // int64_t <> BigInt
100
+ template <> struct JSIConverter<int64_t> {
101
+ static inline double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
102
+ return arg.asBigInt(runtime).asInt64(runtime);
103
+ }
104
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, int64_t arg) {
105
+ return jsi::BigInt::fromInt64(runtime, arg);
106
+ }
107
+ };
108
+
109
+ // uint64_t <> BigInt
110
+ template <> struct JSIConverter<uint64_t> {
111
+ static inline double fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
112
+ return arg.asBigInt(runtime).asUint64(runtime);
113
+ }
114
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, uint64_t arg) {
115
+ return jsi::BigInt::fromUint64(runtime, arg);
116
+ }
117
+ };
118
+
119
+ // bool <> boolean
120
+ template <> struct JSIConverter<bool> {
121
+ static inline bool fromJSI(jsi::Runtime&, const jsi::Value& arg) {
122
+ return arg.asBool();
123
+ }
124
+ static inline jsi::Value toJSI(jsi::Runtime&, bool arg) {
125
+ return jsi::Value(arg);
126
+ }
127
+ };
128
+
129
+ // std::string <> string
130
+ template <> struct JSIConverter<std::string> {
131
+ static inline std::string fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
132
+ return arg.asString(runtime).utf8(runtime);
133
+ }
134
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::string& arg) {
135
+ return jsi::String::createFromUtf8(runtime, arg);
136
+ }
137
+ };
138
+
139
+ // std::optional<T> <> T | undefined
140
+ template <typename TInner> struct JSIConverter<std::optional<TInner>> {
141
+ static inline std::optional<TInner> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
142
+ if (arg.isUndefined() || arg.isNull()) {
143
+ return std::nullopt;
144
+ } else {
145
+ return JSIConverter<TInner>::fromJSI(runtime, arg);
146
+ }
147
+ }
148
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::optional<TInner>& arg) {
149
+ if (arg == std::nullopt) {
150
+ return jsi::Value::undefined();
151
+ } else {
152
+ return JSIConverter<TInner>::toJSI(runtime, arg.value());
153
+ }
154
+ }
155
+ };
156
+
157
+ // std::future<T> <> Promise<T>
158
+ template <typename TResult> struct JSIConverter<std::future<TResult>> {
159
+ static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
160
+ throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
161
+ }
162
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::future<TResult>&& arg) {
163
+ auto sharedFuture = std::make_shared<std::future<TResult>>(std::move(arg));
164
+ std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
165
+ std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
166
+
167
+ return Promise::createPromise(runtime, [sharedFuture, weakDispatcher](jsi::Runtime& runtime, std::shared_ptr<Promise> promise) {
168
+ // Spawn new async thread to synchronously wait for the `future<T>` to complete
169
+ std::thread waiterThread([promise, &runtime, weakDispatcher, sharedFuture]() {
170
+ // synchronously wait until the `future<T>` completes. we are running on a background task here.
171
+ sharedFuture->wait();
172
+
173
+ // the async function completed successfully, get a JS Dispatcher so we can resolve on JS Thread
174
+ std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
175
+ if (!dispatcher) {
176
+ Logger::log("JSIConverter", "Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed.");
177
+ return;
178
+ }
179
+
180
+ dispatcher->runAsync([&runtime, promise, sharedFuture]() mutable {
181
+ try {
182
+ if constexpr (std::is_void_v<TResult>) {
183
+ // it's returning void, just return undefined to JS
184
+ sharedFuture->get();
185
+ promise->resolve(runtime, jsi::Value::undefined());
186
+ } else {
187
+ // it's returning a custom type, convert it to a jsi::Value
188
+ TResult result = sharedFuture->get();
189
+ jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, result);
190
+ promise->resolve(runtime, std::move(jsResult));
191
+ }
192
+ } catch (const std::exception& exception) {
193
+ // the async function threw an error, reject the promise on JS Thread
194
+ std::string what = exception.what();
195
+ promise->reject(runtime, what);
196
+ } catch (...) {
197
+ // the async function threw a non-std error, try getting it
198
+ std::string name = TypeInfo::getCurrentExceptionName();
199
+ promise->reject(runtime, "Unknown non-std exception: " + name);
200
+ }
201
+
202
+ // This lambda owns the promise shared pointer, and we need to call its
203
+ // destructor on the correct thread here - otherwise it might be called
204
+ // from the waiterThread.
205
+ promise = nullptr;
206
+ });
207
+ });
208
+ waiterThread.detach();
209
+ });
210
+ }
211
+ };
212
+
213
+ template <typename T> struct future_traits {
214
+ using type = void;
215
+ };
216
+ template <typename T> struct future_traits<std::future<T>> {
217
+ using type = T;
218
+ };
219
+ template <typename T> using future_traits_t = typename future_traits<std::remove_reference_t<T>>::type;
220
+
221
+ // [](Args...) -> T {} <> (Args...) => T
222
+ template <typename ReturnType, typename... Args> struct JSIConverter<std::function<ReturnType(Args...)>> {
223
+ // std::future<T> -> T
224
+ using ResultingType = future_traits_t<ReturnType>;
225
+
226
+ static inline ResultingType callJSFunction(jsi::Runtime& runtime, const OwningReference<jsi::Function>& function, const Args&... args) {
227
+ // Throw a lock on the OwningReference<T> so we can guarantee safe access (Hermes GC cannot delete it while `lock` is alive)
228
+ OwningLock<jsi::Function> lock = function.lock();
229
+
230
+ if (!function) {
231
+ if constexpr (std::is_void_v<ResultingType>) {
232
+ // runtime has already been deleted. since this returns void, we can just ignore it being deleted.
233
+ Logger::log("JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
234
+ return;
235
+ } else {
236
+ // runtime has already been deleted, but we are expecting a return value - throw an error in this case.
237
+ throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
238
+ }
239
+ }
240
+
241
+ if constexpr (std::is_void_v<ResultingType>) {
242
+ // It returns void. Just call the function
243
+ function->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
244
+ } else {
245
+ // It returns some kind of value - call the function, and convert the return value.
246
+ jsi::Value result = function->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
247
+ return JSIConverter<ResultingType>::fromJSI(runtime, std::move(result));
248
+ }
249
+ }
250
+
251
+ static inline std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
252
+ // Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
253
+ auto cache = JSICache<jsi::Function>::getOrCreateCache(runtime);
254
+ jsi::Function function = arg.asObject(runtime).asFunction(runtime);
255
+ OwningReference<jsi::Function> sharedFunction = cache.makeGlobal(std::move(function));
256
+
257
+ std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
258
+ std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
259
+
260
+ // Create a C++ function that can be called by the consumer.
261
+ // This will call the jsi::Function if it is still alive.
262
+ return [&runtime, weakDispatcher, sharedFunction = std::move(sharedFunction)](Args... args) -> ReturnType {
263
+ // Try to get the JS Dispatcher if the Runtime is still alive
264
+ std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
265
+ if (!dispatcher) {
266
+ if constexpr (std::is_void_v<ResultingType>) {
267
+ Logger::log("JSIConverter", "Tried calling void(..) function, but the JS Dispatcher has already been deleted by JS!");
268
+ return;
269
+ } else {
270
+ throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
271
+ }
272
+ }
273
+
274
+ if constexpr (std::is_void_v<ResultingType>) {
275
+ dispatcher->runAsync([&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() {
276
+ callJSFunction(runtime, sharedFunction, args...);
277
+ });
278
+ } else {
279
+ return dispatcher->runAsyncAwaitable<ResultingType>(
280
+ [&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() -> ResultingType {
281
+ return callJSFunction(runtime, sharedFunction, args...);
282
+ });
283
+ }
284
+ };
285
+ }
286
+
287
+ template <size_t... Is>
288
+ static inline jsi::Value callHybridFunction(const std::function<ReturnType(Args...)>& function, jsi::Runtime& runtime,
289
+ const jsi::Value* args, std::index_sequence<Is...>) {
290
+ if constexpr (std::is_void_v<ReturnType>) {
291
+ // it is a void function (will return undefined in JS)
292
+ function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
293
+ return jsi::Value::undefined();
294
+ } else {
295
+ // it is a custom type, parse it to a JS value
296
+ ReturnType result = function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
297
+ return JSIConverter<ReturnType>::toJSI(runtime, result);
298
+ }
299
+ }
300
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::function<ReturnType(Args...)>&& function) {
301
+ jsi::HostFunctionType jsFunction = [function = std::move(function)](jsi::Runtime& runtime, const jsi::Value& thisValue,
302
+ const jsi::Value* args, size_t count) -> jsi::Value {
303
+ if (count != sizeof...(Args)) [[unlikely]] {
304
+ throw jsi::JSError(runtime, "Function expected " + std::to_string(sizeof...(Args)) + " arguments, but received " +
305
+ std::to_string(count) + "!");
306
+ }
307
+ return callHybridFunction(function, runtime, args, std::index_sequence_for<Args...>{});
308
+ };
309
+ return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "hostFunction"), sizeof...(Args), jsFunction);
310
+ }
311
+ };
312
+
313
+ // std::vector<T> <> T[]
314
+ template <typename ElementType> struct JSIConverter<std::vector<ElementType>> {
315
+ static inline std::vector<ElementType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
316
+ jsi::Array array = arg.asObject(runtime).asArray(runtime);
317
+ size_t length = array.size(runtime);
318
+
319
+ std::vector<ElementType> vector;
320
+ vector.reserve(length);
321
+ for (size_t i = 0; i < length; ++i) {
322
+ jsi::Value elementValue = array.getValueAtIndex(runtime, i);
323
+ vector.emplace_back(JSIConverter<ElementType>::fromJSI(runtime, elementValue));
324
+ }
325
+ return vector;
326
+ }
327
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::vector<ElementType>& vector) {
328
+ jsi::Array array(runtime, vector.size());
329
+ for (size_t i = 0; i < vector.size(); i++) {
330
+ jsi::Value value = JSIConverter<ElementType>::toJSI(runtime, vector[i]);
331
+ array.setValueAtIndex(runtime, i, std::move(value));
332
+ }
333
+ return array;
334
+ }
335
+ };
336
+
337
+ // std::unordered_map<std::string, T> <> Record<string, T>
338
+ template <typename ValueType> struct JSIConverter<std::unordered_map<std::string, ValueType>> {
339
+ static inline std::unordered_map<std::string, ValueType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
340
+ jsi::Object object = arg.asObject(runtime);
341
+ jsi::Array propertyNames = object.getPropertyNames(runtime);
342
+ size_t length = propertyNames.size(runtime);
343
+
344
+ std::unordered_map<std::string, ValueType> map;
345
+ map.reserve(length);
346
+ for (size_t i = 0; i < length; ++i) {
347
+ std::string key = propertyNames.getValueAtIndex(runtime, i).asString(runtime).utf8(runtime);
348
+ jsi::Value value = object.getProperty(runtime, key.c_str());
349
+ map.emplace(key, JSIConverter<ValueType>::fromJSI(runtime, value));
350
+ }
351
+ return map;
352
+ }
353
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::unordered_map<std::string, ValueType>& map) {
354
+ jsi::Object object(runtime);
355
+ for (const auto& pair : map) {
356
+ jsi::Value value = JSIConverter<ValueType>::toJSI(runtime, pair.second);
357
+ object.setProperty(runtime, pair.first.c_str(), std::move(value));
358
+ }
359
+ return object;
360
+ }
361
+ };
362
+
363
+ template <typename... Types> struct JSIConverter<std::tuple<Types...>> {
364
+ static inline std::tuple<Types...> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
365
+ jsi::Object object = value.asObject(runtime);
366
+ jsi::Array array = object.asArray(runtime);
367
+ if (array.size(runtime) != sizeof...(Types)) [[unlikely]] {
368
+ std::string types = TypeInfo::getFriendlyTypenames<Types...>();
369
+ throw std::runtime_error("The given JS Array has " + std::to_string(array.size(runtime)) + " items, but std::tuple<" + types +
370
+ "> expects " + std::to_string(sizeof...(Types)) + " items.");
371
+ }
372
+
373
+ return copyArrayItemsToTuple(runtime, array, std::index_sequence_for<Types...>{});
374
+ }
375
+
376
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::tuple<Types...>& tuple) {
377
+ jsi::Array array(runtime, sizeof...(Types));
378
+ copyTupleItemsToArray(runtime, array, tuple, std::index_sequence_for<Types...>{});
379
+ return array;
380
+ }
381
+
382
+ private:
383
+ template <std::size_t... Is>
384
+ static inline std::tuple<Types...> copyArrayItemsToTuple(jsi::Runtime& runtime, const jsi::Array& array, std::index_sequence<Is...>) {
385
+ return std::make_tuple(JSIConverter<Types>::fromJSI(runtime, array.getValueAtIndex(runtime, Is))...);
386
+ }
387
+
388
+ template <std::size_t... Is>
389
+ static inline void copyTupleItemsToArray(jsi::Runtime& runtime, jsi::Array& array, const std::tuple<Types...>& tuple,
390
+ std::index_sequence<Is...>) {
391
+ ((array.setValueAtIndex(runtime, Is,
392
+ JSIConverter<std::tuple_element_t<Is, std::tuple<Types...>>>::toJSI(runtime, std::get<Is>(tuple)))),
393
+ ...);
394
+ }
395
+ };
396
+
397
+ // Helper struct to check if a type is present in a parameter pack
398
+ template <typename T, typename... Types> struct is_in_pack : std::disjunction<std::is_same<T, Types>...> {};
399
+ template <typename T, typename... Types> inline constexpr bool is_in_pack_v = is_in_pack<T, Types...>::value;
400
+
401
+ // std::variant<A, B, C> <> A | B | C
402
+ template <typename... Types> struct JSIConverter<std::variant<Types...>> {
403
+ static inline std::variant<Types...> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
404
+ if (value.isNull()) {
405
+ if constexpr (is_in_pack_v<std::monostate, Types...>) {
406
+ return std::monostate();
407
+ } else {
408
+ throw typeNotSupportedError("null");
409
+ }
410
+ } else if (value.isBool()) {
411
+ if constexpr (is_in_pack_v<bool, Types...>) {
412
+ return JSIConverter<bool>::fromJSI(runtime, value);
413
+ } else {
414
+ throw typeNotSupportedError("boolean");
415
+ }
416
+ } else if (value.isNumber()) {
417
+ if constexpr (is_in_pack_v<double, Types...>) {
418
+ return JSIConverter<double>::fromJSI(runtime, value);
419
+ } else {
420
+ throw typeNotSupportedError("number");
421
+ }
422
+ } else if (value.isString()) {
423
+ if constexpr (is_in_pack_v<std::string, Types...>) {
424
+ return JSIConverter<std::string>::fromJSI(runtime, value);
425
+ } else {
426
+ throw typeNotSupportedError("string");
427
+ }
428
+ } else if (value.isBigInt()) {
429
+ if constexpr (is_in_pack_v<int64_t, Types...>) {
430
+ return JSIConverter<int64_t>::fromJSI(runtime, value);
431
+ } else {
432
+ throw typeNotSupportedError("bigint");
433
+ }
434
+ } else if (value.isObject()) {
435
+ jsi::Object valueObj = value.getObject(runtime);
436
+ if (valueObj.isArray(runtime)) {
437
+ if constexpr (is_in_pack_v<std::vector<AnyValue>, Types...>) {
438
+ return JSIConverter<std::vector<AnyValue>>::fromJSI(runtime, value);
439
+ } else {
440
+ throw typeNotSupportedError("array[]");
441
+ }
442
+ } else {
443
+ if constexpr (is_in_pack_v<std::unordered_map<std::string, AnyValue>, Types...>) {
444
+ return JSIConverter<std::unordered_map<std::string, AnyValue>>::fromJSI(runtime, value);
445
+ } else {
446
+ throw typeNotSupportedError("object{}");
447
+ }
448
+ }
449
+ } else {
450
+ std::string stringRepresentation = value.toString(runtime).utf8(runtime);
451
+ throw std::runtime_error("Cannot convert \"" + stringRepresentation + "\" to std::variant<...>!");
452
+ }
453
+ }
454
+
455
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::variant<Types...>& variant) {
456
+ return std::visit([&runtime](const auto& val) { return JSIConverter<std::decay_t<decltype(val)>>::toJSI(runtime, val); }, variant);
457
+ }
458
+
459
+ private:
460
+ static inline std::runtime_error typeNotSupportedError(const std::string& type) {
461
+ std::string types = TypeInfo::getFriendlyTypenames<Types...>();
462
+ return std::runtime_error(type + " is not supported in variant<" + types + ">!");
463
+ }
464
+ };
465
+
466
+ // AnyValue <> Record<K, V>
467
+ template <> struct JSIConverter<AnyValue> {
468
+ static inline AnyValue fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
469
+ return JSIConverter<AnyValue::variant>::fromJSI(runtime, arg);
470
+ }
471
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const AnyValue& value) {
472
+ return JSIConverter<std::variant<AnyValue::variant>>::toJSI(runtime, value);
473
+ }
474
+ };
475
+
476
+ // AnyMap <> Record<K, V>
477
+ template <> struct JSIConverter<std::shared_ptr<AnyMap>> {
478
+ static inline std::shared_ptr<AnyMap> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
479
+ jsi::Object object = arg.asObject(runtime);
480
+ jsi::Array propNames = object.getPropertyNames(runtime);
481
+ size_t size = propNames.size(runtime);
482
+ std::shared_ptr<AnyMap> map = AnyMap::make();
483
+ for (size_t i = 0; i < size; i++) {
484
+ jsi::String jsKey = propNames.getValueAtIndex(runtime, i).getString(runtime);
485
+ jsi::Value jsValue = object.getProperty(runtime, jsKey);
486
+ map->setAny(jsKey.utf8(runtime), JSIConverter<AnyValue>::fromJSI(runtime, jsValue));
487
+ }
488
+ return map;
489
+ }
490
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<AnyMap> map) {
491
+ jsi::Object object(runtime);
492
+ for (const auto& item : map->getMap()) {
493
+ jsi::String key = jsi::String::createFromUtf8(runtime, item.first);
494
+ jsi::Value value = JSIConverter<AnyValue>::toJSI(runtime, item.second);
495
+ object.setProperty(runtime, std::move(key), std::move(value));
496
+ }
497
+ return object;
498
+ }
499
+ };
500
+
501
+ // MutableBuffer <> ArrayBuffer
502
+ template <> struct JSIConverter<std::shared_ptr<jsi::MutableBuffer>> {
503
+ static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
504
+ jsi::Object object = arg.asObject(runtime);
505
+ if (!object.isArrayBuffer(runtime)) [[unlikely]] {
506
+ throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) + "\" is not an ArrayBuffer!");
507
+ }
508
+ jsi::ArrayBuffer arrayBuffer = object.getArrayBuffer(runtime);
509
+ return std::make_shared<ArrayBuffer>(arrayBuffer.data(runtime), arrayBuffer.size(runtime), false);
510
+ }
511
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<jsi::MutableBuffer> buffer) {
512
+ return jsi::ArrayBuffer(runtime, buffer);
513
+ }
514
+ };
515
+
516
+ // HybridObject <> {}
517
+ template <typename T> struct is_shared_ptr_to_host_object : std::false_type {};
518
+ template <typename T> struct is_shared_ptr_to_host_object<std::shared_ptr<T>> : std::is_base_of<jsi::HostObject, T> {};
519
+ template <typename T> struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_host_object<T>::value>> {
520
+ using TPointee = typename T::element_type;
521
+
522
+ static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
523
+ #if DO_NULL_CHECKS
524
+ if (arg.isUndefined()) [[unlikely]] {
525
+ throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
526
+ }
527
+ if (!arg.isObject()) [[unlikely]] {
528
+ std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
529
+ throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not an object!"));
530
+ }
531
+ #endif
532
+ jsi::Object object = arg.asObject(runtime);
533
+ #if DO_NULL_CHECKS
534
+ if (!object.isHostObject<TPointee>(runtime)) [[unlikely]] {
535
+ std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
536
+ throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different HostObject<T>!"));
537
+ }
538
+ #endif
539
+ return object.asHostObject<TPointee>(runtime);
540
+ }
541
+
542
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
543
+ #if DO_NULL_CHECKS
544
+ if (arg == nullptr) [[unlikely]] {
545
+ std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
546
+ throw jsi::JSError(runtime, "Cannot convert nullptr to HostObject<" + typeName + ">!");
547
+ }
548
+ #endif
549
+ if constexpr (std::is_base_of_v<HybridObject, TPointee>) {
550
+ // It's a HybridObject - use it's internal constructor which caches jsi::Objects for proper memory management!
551
+ return arg->toObject(runtime);
552
+ } else {
553
+ // It's any other kind of jsi::HostObject - just create it as normal.
554
+ return jsi::Object::createFromHostObject(runtime, arg);
555
+ }
556
+ }
557
+
558
+ private:
559
+ static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
560
+ std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
561
+ return "Cannot convert \"" + typeDescription + "\" to HostObject<" + typeName + ">! " + reason;
562
+ }
563
+ };
564
+
565
+ // NativeState <> {}
566
+ template <typename T> struct is_shared_ptr_to_native_state : std::false_type {};
567
+ template <typename T> struct is_shared_ptr_to_native_state<std::shared_ptr<T>> : std::is_base_of<jsi::NativeState, T> {};
568
+ template <typename T> struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_native_state<T>::value>> {
569
+ using TPointee = typename T::element_type;
570
+
571
+ static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
572
+ #if DO_NULL_CHECKS
573
+ if (arg.isUndefined()) [[unlikely]] {
574
+ throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
575
+ }
576
+ if (!arg.isObject()) [[unlikely]] {
577
+ std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
578
+ throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not an object!"));
579
+ }
580
+ #endif
581
+ jsi::Object object = arg.asObject(runtime);
582
+ #if DO_NULL_CHECKS
583
+ if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
584
+ std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
585
+ throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different NativeState<T>!"));
586
+ }
587
+ #endif
588
+ return object.getNativeState<TPointee>(runtime);
589
+ }
590
+
591
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
592
+ #if DO_NULL_CHECKS
593
+ if (arg == nullptr) [[unlikely]] {
594
+ std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
595
+ throw jsi::JSError(runtime, "Cannot convert nullptr to NativeState<" + typeName + ">!");
596
+ }
597
+ #endif
598
+ jsi::Object object(runtime);
599
+ object.setNativeState(runtime, arg);
600
+ return object;
601
+ }
602
+
603
+ private:
604
+ static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
605
+ std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
606
+ return "Cannot convert \"" + typeDescription + "\" to NativeState<" + typeName + ">! " + reason;
607
+ }
608
+ };
609
+
610
+ } // namespace margelo::nitro
@@ -0,0 +1,54 @@
1
+ #include "Promise.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
+ Promise::Promise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter) {
11
+ auto functionCache = JSICache<jsi::Function>::getOrCreateCache(runtime);
12
+ _resolver = functionCache.makeGlobal(std::move(resolver));
13
+ _rejecter = functionCache.makeGlobal(std::move(rejecter));
14
+ }
15
+
16
+ jsi::Value Promise::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<Promise>(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 Promise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
38
+ if (!_resolver) {
39
+ Logger::log(TAG, "Promise resolver function has already been deleted! Ignoring call..");
40
+ return;
41
+ }
42
+ _resolver->call(runtime, std::move(result));
43
+ }
44
+
45
+ void Promise::reject(jsi::Runtime& runtime, std::string message) {
46
+ if (!_rejecter) {
47
+ Logger::log(TAG, "Promise rejecter function has already been deleted! Ignoring call..");
48
+ return;
49
+ }
50
+ jsi::JSError error(runtime, message);
51
+ _rejecter->call(runtime, error.value());
52
+ }
53
+
54
+ } // namespace margelo::nitro