react-native-nitro-modules 0.23.0 → 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.
- package/NitroModules.podspec +1 -0
- package/README.md +6 -0
- package/android/src/main/cpp/registry/DefaultConstructableObject.hpp +1 -1
- package/cpp/core/ArrayBuffer.cpp +3 -5
- package/cpp/core/ArrayBuffer.hpp +2 -2
- package/cpp/core/HybridFunction.hpp +2 -1
- package/cpp/core/HybridObject.cpp +3 -1
- package/cpp/core/Promise.hpp +8 -5
- package/cpp/jsi/JSICache.hpp +3 -3
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +1 -1
- package/cpp/jsi/JSIConverter+Exception.hpp +4 -1
- package/cpp/jsi/JSIConverter+Function.hpp +24 -67
- package/cpp/jsi/JSIConverter+Promise.hpp +3 -2
- package/cpp/prototype/HybridObjectPrototype.cpp +12 -9
- package/cpp/templates/PromiseType.hpp +12 -0
- package/cpp/threading/Dispatcher.cpp +7 -5
- package/cpp/utils/BorrowingReference.hpp +40 -43
- package/cpp/utils/JSCallback.hpp +132 -0
- package/cpp/utils/NitroDefines.hpp +1 -1
- package/cpp/utils/NitroTypeInfo.cpp +80 -0
- package/cpp/utils/NitroTypeInfo.hpp +8 -65
- package/cpp/utils/WeakReference.hpp +6 -7
- package/cpp/views/CachedProp.hpp +2 -1
- package/ios/turbomodule/NativeNitroModules+NewArch.mm +1 -1
- package/ios/utils/RuntimeError.hpp +1 -1
- package/lib/commonjs/Sync.js +2 -0
- package/lib/commonjs/Sync.js.map +1 -0
- package/lib/commonjs/index.js +11 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/Sync.js +2 -0
- package/lib/module/Sync.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/typescript/Sync.d.ts +11 -0
- package/lib/typescript/Sync.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Sync.ts +8 -0
- package/src/index.ts +1 -0
- package/cpp/utils/OwningLock.hpp +0 -56
package/NitroModules.podspec
CHANGED
package/README.md
CHANGED
|
@@ -181,6 +181,12 @@ The following C++ / JS types are supported out of the box:
|
|
|
181
181
|
<td><code>(T...) -> <a href="./ios/core/Promise.swift">Promise<T></a></code></td>
|
|
182
182
|
<td><code>(T...) -> <a href="./android/src/main/java/com/margelo/nitro/core/Promise.kt">Promise<T></a></code></td>
|
|
183
183
|
</tr>
|
|
184
|
+
<tr>
|
|
185
|
+
<td><code>Sync<(T...) => R></code></td>
|
|
186
|
+
<td><code>std::function<R (T...)></code></td>
|
|
187
|
+
<td><code>@escaping (T...) -> R</code></td>
|
|
188
|
+
<td><code>(T...) -> R</code></td>
|
|
189
|
+
</tr>
|
|
184
190
|
<tr>
|
|
185
191
|
<td><code>Error</code></td>
|
|
186
192
|
<td><code>std::exception_ptr</code></td>
|
|
@@ -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 \"
|
|
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;
|
package/cpp/core/ArrayBuffer.cpp
CHANGED
|
@@ -61,7 +61,7 @@ bool NativeArrayBuffer::isOwner() const noexcept {
|
|
|
61
61
|
|
|
62
62
|
// 3. JSArrayBuffer
|
|
63
63
|
|
|
64
|
-
JSArrayBuffer::JSArrayBuffer(jsi::Runtime
|
|
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(
|
|
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(
|
|
94
|
+
return _jsReference->size(_runtime);
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
bool JSArrayBuffer::isOwner() const noexcept {
|
package/cpp/core/ArrayBuffer.hpp
CHANGED
|
@@ -122,7 +122,7 @@ private:
|
|
|
122
122
|
*/
|
|
123
123
|
class JSArrayBuffer final : public ArrayBuffer {
|
|
124
124
|
public:
|
|
125
|
-
explicit JSArrayBuffer(jsi::Runtime
|
|
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
|
|
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,
|
|
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:
|
|
@@ -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
|
-
|
|
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
|
package/cpp/core/Promise.hpp
CHANGED
|
@@ -122,7 +122,8 @@ public:
|
|
|
122
122
|
*/
|
|
123
123
|
void reject(const std::exception_ptr& exception) {
|
|
124
124
|
if (exception == nullptr) [[unlikely]] {
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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
|
}
|
package/cpp/jsi/JSICache.hpp
CHANGED
|
@@ -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,
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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>(
|
|
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
|
|
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
|
|
29
|
-
struct JSIConverter<std::function<
|
|
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
|
|
31
|
+
using ActualR = std::conditional_t<isAsync, promise_type_v<R>, R>;
|
|
32
32
|
|
|
33
|
-
static inline std::function<
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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<
|
|
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<
|
|
84
|
-
std::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<
|
|
122
|
-
|
|
123
|
-
if constexpr (std::is_void_v<
|
|
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
|
-
|
|
130
|
-
return JSIConverter<
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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, *
|
|
84
|
+
return jsi::Value(runtime, *sharedObject);
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
jsi::Value HybridObjectPrototype::getPrototype(jsi::Runtime& runtime) {
|
|
@@ -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
|
-
|
|
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);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
10
|
#include "NitroDefines.hpp"
|
|
11
|
-
#include "
|
|
11
|
+
#include "NitroTypeInfo.hpp"
|
|
12
12
|
#include "ReferenceState.hpp"
|
|
13
13
|
#include "WeakReference.hpp"
|
|
14
14
|
#include <atomic>
|
|
@@ -18,19 +18,15 @@
|
|
|
18
18
|
namespace margelo::nitro {
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
An `BorrowingReference<T>` is a smart-pointer that holds a strong reference to a pointer.
|
|
22
|
-
You can have multiple `BorrowingReference<T>` instances point to the same pointer, as they internally keep a ref-count.
|
|
23
|
-
As opposed to a `shared_ptr<T>`, an `BorrowingReference<T>` can also be imperatively manually deleted, even if there
|
|
24
|
-
are multiple strong references still holding onto the pointer.
|
|
25
|
-
This is useful in cases where the `BorrowingReference` might keep a reference alive, but an external value holder
|
|
26
|
-
is actually responsible for truly deleting the underlying value - like a `jsi::Runtime` for a `jsi::Value`.
|
|
27
|
-
|
|
28
|
-
An `BorrowingReference<T>` can be weakified, which gives the user a `WeakReference<T>`.
|
|
29
|
-
A `WeakReference<T>` can be locked to get an `BorrowingReference<T>` again, assuming it has not been deleted yet.
|
|
30
|
-
|
|
31
|
-
A `BorrowingReference<T>` can also be locked using `lock()`, which gives the user an `OwningLock<T>`.
|
|
32
|
-
Only an `OwningLock<T>` guarantees safe access to the underlying value reference, as any external value holders
|
|
33
|
-
that might try to delete the value will have to wait until the lock is freed again.
|
|
21
|
+
* An `BorrowingReference<T>` is a smart-pointer that holds a strong reference to a pointer.
|
|
22
|
+
* You can have multiple `BorrowingReference<T>` instances point to the same pointer, as they internally keep a ref-count.
|
|
23
|
+
* As opposed to a `shared_ptr<T>`, an `BorrowingReference<T>` can also be imperatively manually deleted, even if there
|
|
24
|
+
* are multiple strong references still holding onto the pointer.
|
|
25
|
+
* This is useful in cases where the `BorrowingReference` might keep a reference alive, but an external value holder
|
|
26
|
+
* is actually responsible for truly deleting the underlying value - like a `jsi::Runtime` for a `jsi::Value`.
|
|
27
|
+
*
|
|
28
|
+
* An `BorrowingReference<T>` can be weakified, which gives the user a `WeakReference<T>`.
|
|
29
|
+
* A `WeakReference<T>` can be locked to get an `BorrowingReference<T>` again, assuming it has not been deleted yet.
|
|
34
30
|
*/
|
|
35
31
|
template <typename T>
|
|
36
32
|
class BorrowingReference final {
|
|
@@ -46,7 +42,7 @@ public:
|
|
|
46
42
|
}
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
BorrowingReference(BorrowingReference&& ref) : _value(ref._value), _state(ref._state) {
|
|
45
|
+
BorrowingReference(BorrowingReference&& ref) noexcept : _value(ref._value), _state(ref._state) {
|
|
50
46
|
ref._value = nullptr;
|
|
51
47
|
ref._state = nullptr;
|
|
52
48
|
}
|
|
@@ -76,7 +72,7 @@ public:
|
|
|
76
72
|
|
|
77
73
|
private:
|
|
78
74
|
// WeakReference<T> -> BorrowingReference<T> Lock-constructor
|
|
79
|
-
BorrowingReference(const WeakReference<T>& ref) : _value(ref._value), _state(ref._state) {
|
|
75
|
+
explicit BorrowingReference(const WeakReference<T>& ref) : _value(ref._value), _state(ref._state) {
|
|
80
76
|
_state->strongRefCount++;
|
|
81
77
|
}
|
|
82
78
|
|
|
@@ -107,7 +103,7 @@ public:
|
|
|
107
103
|
|
|
108
104
|
public:
|
|
109
105
|
/**
|
|
110
|
-
Casts this `BorrowingReference<T>` to a `BorrowingReference<C>`.
|
|
106
|
+
* Casts this `BorrowingReference<T>` to a `BorrowingReference<C>`.
|
|
111
107
|
*/
|
|
112
108
|
template <typename C>
|
|
113
109
|
BorrowingReference<C> as() {
|
|
@@ -116,25 +112,15 @@ public:
|
|
|
116
112
|
|
|
117
113
|
public:
|
|
118
114
|
/**
|
|
119
|
-
|
|
120
|
-
safe access to `BorrowingReference<T>`.
|
|
121
|
-
Other threads (e.g. the Hermes garbage collector) cannot delete the `BorrowingReference<T>`
|
|
122
|
-
as long as the `OwningLock<T>` is still alive.
|
|
115
|
+
* Get whether the `BorrowingReference<T>` is still pointing to a valid value, or not.
|
|
123
116
|
*/
|
|
124
117
|
[[nodiscard]]
|
|
125
|
-
OwningLock<T> lock() const {
|
|
126
|
-
return OwningLock<T>(*this);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
Get whether the `BorrowingReference<T>` is still pointing to a valid value, or not.
|
|
131
|
-
*/
|
|
132
118
|
inline bool hasValue() const {
|
|
133
119
|
return _value != nullptr && !_state->isDeleted;
|
|
134
120
|
}
|
|
135
121
|
|
|
136
122
|
/**
|
|
137
|
-
Get a borrowing (or "weak") reference to this owning reference
|
|
123
|
+
* Get a borrowing (or "weak") reference to this owning reference
|
|
138
124
|
*/
|
|
139
125
|
[[nodiscard]]
|
|
140
126
|
WeakReference<T> weak() const {
|
|
@@ -142,10 +128,8 @@ public:
|
|
|
142
128
|
}
|
|
143
129
|
|
|
144
130
|
/**
|
|
145
|
-
Delete and destroy the value this BorrowingReference is pointing to.
|
|
146
|
-
This can even be called if there are still multiple strong references to the value.
|
|
147
|
-
|
|
148
|
-
This will block as long as one or more `OwningLock<T>`s of this `BorrowingReference<T>` are still alive.
|
|
131
|
+
* Delete and destroy the value this BorrowingReference is pointing to.
|
|
132
|
+
* This can even be called if there are still multiple strong references to the value.
|
|
149
133
|
*/
|
|
150
134
|
void destroy() {
|
|
151
135
|
std::unique_lock lock(_state->mutex);
|
|
@@ -154,40 +138,55 @@ public:
|
|
|
154
138
|
}
|
|
155
139
|
|
|
156
140
|
public:
|
|
157
|
-
|
|
158
|
-
return hasValue();
|
|
159
|
-
}
|
|
160
|
-
|
|
141
|
+
// Dereference (*)
|
|
161
142
|
inline T& operator*() const {
|
|
162
143
|
#ifdef NITRO_DEBUG
|
|
163
144
|
if (!hasValue()) [[unlikely]] {
|
|
164
|
-
|
|
145
|
+
std::string typeName = TypeInfo::getFriendlyTypename<T>(true);
|
|
146
|
+
throw std::runtime_error("Tried to dereference (*) nullptr BorrowingReference<" + typeName + ">!");
|
|
165
147
|
}
|
|
166
148
|
#endif
|
|
167
149
|
return *_value;
|
|
168
150
|
}
|
|
169
151
|
|
|
152
|
+
// Dereference (->)
|
|
170
153
|
inline T* operator->() const {
|
|
171
154
|
#ifdef NITRO_DEBUG
|
|
172
155
|
if (!hasValue()) [[unlikely]] {
|
|
173
|
-
|
|
156
|
+
std::string typeName = TypeInfo::getFriendlyTypename<T>(true);
|
|
157
|
+
throw std::runtime_error("Tried to dereference (->) nullptr BorrowingReference<" + typeName + ">!");
|
|
174
158
|
}
|
|
175
159
|
#endif
|
|
176
160
|
return _value;
|
|
177
161
|
}
|
|
178
162
|
|
|
163
|
+
// null-check (bool)
|
|
164
|
+
explicit inline operator bool() const {
|
|
165
|
+
return hasValue();
|
|
166
|
+
}
|
|
167
|
+
// null-check (== nullptr)
|
|
168
|
+
inline bool operator==(std::nullptr_t) const {
|
|
169
|
+
return !hasValue();
|
|
170
|
+
}
|
|
171
|
+
// null-check (!= nullptr)
|
|
172
|
+
inline bool operator!=(std::nullptr_t) const {
|
|
173
|
+
return hasValue();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// comparison (== *)
|
|
179
177
|
inline bool operator==(T* other) const {
|
|
180
178
|
return _value == other;
|
|
181
179
|
}
|
|
182
|
-
|
|
180
|
+
// comparison (!= *)
|
|
183
181
|
inline bool operator!=(T* other) const {
|
|
184
182
|
return _value != other;
|
|
185
183
|
}
|
|
186
184
|
|
|
185
|
+
// comparison (== BorrowingReference<T>)
|
|
187
186
|
inline bool operator==(const BorrowingReference<T>& other) const {
|
|
188
187
|
return _value == other._value;
|
|
189
188
|
}
|
|
190
|
-
|
|
189
|
+
// comparison (!= BorrowingReference<T>)
|
|
191
190
|
inline bool operator!=(const BorrowingReference<T>& other) const {
|
|
192
191
|
return _value != other._value;
|
|
193
192
|
}
|
|
@@ -198,7 +197,6 @@ private:
|
|
|
198
197
|
// free the full memory if there are no more references at all
|
|
199
198
|
delete _state;
|
|
200
199
|
_state = nullptr;
|
|
201
|
-
return;
|
|
202
200
|
}
|
|
203
201
|
}
|
|
204
202
|
|
|
@@ -214,7 +212,6 @@ private:
|
|
|
214
212
|
|
|
215
213
|
public:
|
|
216
214
|
friend class WeakReference<T>;
|
|
217
|
-
friend class OwningLock<T>;
|
|
218
215
|
|
|
219
216
|
private:
|
|
220
217
|
T* _value;
|