9
9
#include " Promise.hpp"
10
10
#include " PromiseFactory.hpp"
11
11
#include " Dispatcher.hpp"
12
+ #include " FunctionCache.hpp"
12
13
#include < array>
13
14
#include < future>
14
15
#include < jsi/jsi.h>
22
23
23
24
namespace margelo {
24
25
26
+ /* *
27
+ The JSIConverter<T> class can convert any type from and to a jsi::Value.
28
+ It uses templates to statically create fromJSI/toJSI methods, and will throw compile-time errors
29
+ if a given type is not convertable.
30
+ Value types, custom types (HostObjects), and even functions with any number of arguments/types are supported.
31
+ This type can be extended by just creating a new template for JSIConverter in a header.
32
+ */
33
+
25
34
using namespace facebook ;
26
35
27
36
// Unknown type (error)
@@ -149,12 +158,13 @@ template <typename TResult> struct JSIConverter<std::future<TResult>> {
149
158
}
150
159
static jsi::Value toJSI (jsi::Runtime& runtime, std::future<TResult>&& arg) {
151
160
auto sharedFuture = std::make_shared<std::future<TResult>>(std::move (arg));
152
- return PromiseFactory::createPromise (runtime, [sharedFuture = std::move (sharedFuture)](jsi::Runtime& runtime,
153
- std::shared_ptr<Promise> promise,
154
- std::shared_ptr<Dispatcher> dispatcher) {
155
- // Spawn new async thread to wait for the result
156
- std::thread waiterThread ([promise, &runtime, dispatcher, sharedFuture = std::move (sharedFuture)]() {
157
- // wait until the future completes. we are running on a background task here.
161
+ auto dispatcher = Dispatcher::getRuntimeGlobalDispatcher (runtime);
162
+
163
+ return Promise::createPromise (runtime, [sharedFuture, dispatcher](jsi::Runtime& runtime,
164
+ std::shared_ptr<Promise> promise) {
165
+ // Spawn new async thread to synchronously wait for the `future<T>` to complete
166
+ std::thread waiterThread ([promise, &runtime, dispatcher, sharedFuture]() {
167
+ // synchronously wait until the `future<T>` completes. we are running on a background task here.
158
168
sharedFuture->wait ();
159
169
160
170
// the async function completed successfully, resolve the promise on JS Thread
@@ -163,27 +173,27 @@ template <typename TResult> struct JSIConverter<std::future<TResult>> {
163
173
if constexpr (std::is_same_v<TResult, void >) {
164
174
// it's returning void, just return undefined to JS
165
175
sharedFuture->get ();
166
- promise->resolve (jsi::Value::undefined ());
176
+ promise->resolve (runtime, jsi::Value::undefined ());
167
177
} else {
168
178
// it's returning a custom type, convert it to a jsi::Value
169
179
TResult result = sharedFuture->get ();
170
180
jsi::Value jsResult = JSIConverter<TResult>::toJSI (runtime, result);
171
- promise->resolve (std::move (jsResult));
181
+ promise->resolve (runtime, std::move (jsResult));
172
182
}
173
183
} catch (const std::exception& exception) {
174
184
// the async function threw an error, reject the promise on JS Thread
175
185
std::string what = exception.what ();
176
- promise->reject (what);
186
+ promise->reject (runtime, what);
177
187
} catch (...) {
178
188
// the async function threw a non-std error, try getting it
179
189
#if __has_include(<cxxabi.h>)
180
190
std::string name = __cxxabiv1::__cxa_current_exception_type ()->name ();
181
191
#else
182
- std::string name = " <unknown>" ;
192
+ std::string name = " <unknown>" ;
183
193
#endif
184
- promise->reject (" Unknown non-std exception: " + name);
194
+ promise->reject (runtime, " Unknown non-std exception: " + name);
185
195
}
186
-
196
+
187
197
// This lambda owns the promise shared pointer, and we need to call its
188
198
// destructor on the correct thread here - otherwise it might be called
189
199
// from the waiterThread.
@@ -198,17 +208,31 @@ template <typename TResult> struct JSIConverter<std::future<TResult>> {
198
208
// [](Args...) -> T {} <> (Args...) => T
199
209
template <typename ReturnType, typename ... Args> struct JSIConverter <std::function<ReturnType(Args...)>> {
200
210
static std::function<ReturnType(Args...)> fromJSI (jsi::Runtime& runtime, const jsi::Value& arg) {
211
+ // Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
212
+ auto cache = FunctionCache::getOrCreateCache (runtime).lock ();
201
213
jsi::Function function = arg.getObject (runtime).getFunction (runtime);
202
-
203
- // TODO: Weakify this using a RuntimeWatch so it is safely managed by the Runtime, not by us.
204
- auto sharedFunction = std::make_shared<jsi::Function>(std::move (function));
214
+ auto sharedFunction = cache->makeGlobal (std::move (function));
215
+
216
+ // Create a C++ function that can be called by the consumer.
217
+ // This will call the jsi::Function if it is still alive.
205
218
return [&runtime, sharedFunction](Args... args) -> ReturnType {
206
- jsi::Value result = sharedFunction->call (runtime, JSIConverter<std::decay_t <Args>>::toJSI (runtime, args)...);
207
219
if constexpr (std::is_same_v<ReturnType, void >) {
208
220
// it is a void function (returns undefined)
221
+ auto function = sharedFunction.lock ();
222
+ if (!function) {
223
+ // runtime has already been deleted. since this returns void, we can just ignore it being deleted.
224
+ return ;
225
+ }
226
+ function->call (runtime, JSIConverter<std::decay_t <Args>>::toJSI (runtime, args)...);
209
227
return ;
210
228
} else {
211
229
// it returns a custom type, parse it from the JSI value.
230
+ auto function = sharedFunction.lock ();
231
+ if (!function) {
232
+ // runtime has already been deleted. since we expect a return value here, we need to throw.
233
+ throw std::runtime_error (" Cannot call the given Function - the Runtime has already been destroyed!" );
234
+ }
235
+ jsi::Value result = function->call (runtime, JSIConverter<std::decay_t <Args>>::toJSI (runtime, args)...);
212
236
return JSIConverter<ReturnType>::fromJSI (runtime, std::move (result));
213
237
}
214
238
};
@@ -294,9 +318,7 @@ template <typename ValueType> struct JSIConverter<std::unordered_map<std::string
294
318
295
319
// HybridObject <> {}
296
320
template <typename T> struct is_shared_ptr_to_host_object : std::false_type {};
297
-
298
321
template <typename T> struct is_shared_ptr_to_host_object <std::shared_ptr<T>> : std::is_base_of<jsi::HostObject, T> {};
299
-
300
322
template <typename T> struct JSIConverter <T, std::enable_if_t <is_shared_ptr_to_host_object<T>::value>> {
301
323
using TPointee = typename T::element_type;
302
324
@@ -354,9 +376,7 @@ template <typename T> struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_h
354
376
355
377
// NativeState <> {}
356
378
template <typename T> struct is_shared_ptr_to_native_state : std::false_type {};
357
-
358
379
template <typename T> struct is_shared_ptr_to_native_state <std::shared_ptr<T>> : std::is_base_of<jsi::NativeState, T> {};
359
-
360
380
template <typename T> struct JSIConverter <T, std::enable_if_t <is_shared_ptr_to_native_state<T>::value>> {
361
381
using TPointee = typename T::element_type;
362
382
0 commit comments