react-native-nitro-modules 0.1.4 → 0.1.6
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/cpp/core/ArrayBuffer.hpp +108 -16
- package/cpp/core/HybridObject.cpp +4 -4
- package/cpp/jsi/JSICache.cpp +55 -0
- package/cpp/jsi/JSICache.hpp +53 -89
- package/cpp/jsi/JSIConverter+AnyMap.hpp +1 -1
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +12 -5
- package/cpp/jsi/JSIConverter+Function.hpp +2 -3
- package/cpp/jsi/JSIConverter+HybridObject.hpp +11 -4
- package/cpp/jsi/JSIConverter+Variant.hpp +2 -2
- package/cpp/jsi/Promise.cpp +7 -3
- package/cpp/templates/IsSharedPtrTo.hpp +28 -0
- package/cpp/utils/OwningReference.hpp +22 -0
- package/package.json +1 -1
- package/cpp/templates/IsHostObject.hpp +0 -27
- package/cpp/templates/IsNativeState.hpp +0 -27
package/cpp/core/ArrayBuffer.hpp
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include "OwningReference.hpp"
|
|
10
11
|
#include <functional>
|
|
11
12
|
#include <jsi/jsi.h>
|
|
13
|
+
#include <thread>
|
|
12
14
|
|
|
13
15
|
namespace margelo::nitro {
|
|
14
16
|
|
|
@@ -23,50 +25,76 @@ static DeleteFn defaultDeleteFn = [](uint8_t* buffer) { delete[] buffer; };
|
|
|
23
25
|
* written to- from both JavaScript and C++.
|
|
24
26
|
* `ArrayBuffer` is not thread-safe and does not lock multi-thread access.
|
|
25
27
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
28
|
+
* `ArrayBuffer` can either be a `JSArrayBuffer`, or a `NativeArrayBuffer`.
|
|
29
|
+
* - `NativeArrayBuffer`: Created from native (C++), and can either own the memory (`isOwner()`), or borrow it.
|
|
30
|
+
* - `JSArrayBuffer`: Received from JS, and will only be alive for as long as the JS Runtime is actually alive.
|
|
29
31
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* Also, an `ArrayBuffer` can either own it's memory, or just borrow it's memory.
|
|
33
|
+
* - Owning = the `ArrayBuffer`'s `data()` is alive as long as the `ArrayBuffer` is alive.
|
|
34
|
+
* When this `ArrayBuffer` gets deleted, it will free the memory.
|
|
35
|
+
* - Borrowed = the `ArrayBuffer`'s `data()` might be deleted at any point from an external source (e.g. the JS garbage collector).
|
|
36
|
+
* When this `ArrayBuffer` gets deleted, the memory will not be freed explicitly, as someone else owns it.
|
|
32
37
|
*/
|
|
33
38
|
class ArrayBuffer : public jsi::MutableBuffer {
|
|
39
|
+
public:
|
|
40
|
+
ArrayBuffer() = default;
|
|
41
|
+
ArrayBuffer(const ArrayBuffer&) = delete;
|
|
42
|
+
ArrayBuffer(ArrayBuffer&&) = delete;
|
|
43
|
+
virtual ~ArrayBuffer() = default;
|
|
44
|
+
|
|
45
|
+
public:
|
|
46
|
+
/**
|
|
47
|
+
* Returns whether this `ArrayBuffer` is actually owning the data,
|
|
48
|
+
* or if it is just borrowed from an external source (either a native
|
|
49
|
+
* memory that we didn't allocate, or from JS - which can be deleted at any point).
|
|
50
|
+
*/
|
|
51
|
+
virtual bool isOwner() const noexcept = 0;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Represents an `ArrayBuffer` that is allocated on the native (C++) side.
|
|
56
|
+
* It can either be "owning" or "borrowing".
|
|
57
|
+
*
|
|
58
|
+
* - Owning = the `ArrayBuffer`'s `data()` is alive as long as the `ArrayBuffer` is alive.
|
|
59
|
+
* When this `ArrayBuffer` gets deleted, it will free the memory.
|
|
60
|
+
* - Borrowed = the `ArrayBuffer`'s `data()` might be deleted at any point from an external source (e.g. the JS garbage collector).
|
|
61
|
+
* When this `ArrayBuffer` gets deleted, the memory will not be freed explicitly, as someone else owns it.
|
|
62
|
+
*
|
|
63
|
+
* It is safe to access `data()` and `size()` from any Thread, but there are no synchronization/mutexes implemented by default.
|
|
64
|
+
*/
|
|
65
|
+
class NativeArrayBuffer : public ArrayBuffer {
|
|
34
66
|
public:
|
|
35
67
|
/**
|
|
36
68
|
* Create a new **owning** `ArrayBuffer`.
|
|
37
69
|
* The `ArrayBuffer` can be kept in memory, as C++ owns the data
|
|
38
70
|
* and will only delete it once this `ArrayBuffer` gets deleted
|
|
39
71
|
*/
|
|
40
|
-
|
|
72
|
+
NativeArrayBuffer(uint8_t* data, size_t size, DeleteFn&& deleteFunc)
|
|
73
|
+
: ArrayBuffer(), _data(data), _size(size), _deleteFunc(std::move(deleteFunc)) {}
|
|
41
74
|
/**
|
|
42
75
|
* Create a new `ArrayBuffer`.
|
|
43
76
|
* If `destroyOnDeletion` is `true`, the `ArrayBuffer` is **owning**, otherwise it is **non-owning**.
|
|
44
77
|
* The `ArrayBuffer` can only be safely kept in memory if it is owning (`isOwning()`).
|
|
45
78
|
*/
|
|
46
|
-
|
|
79
|
+
NativeArrayBuffer(uint8_t* data, size_t size, bool destroyOnDeletion) : ArrayBuffer(), _data(data), _size(size) {
|
|
47
80
|
_deleteFunc = destroyOnDeletion ? defaultDeleteFn : nullptr;
|
|
48
81
|
}
|
|
49
82
|
|
|
50
|
-
|
|
51
|
-
ArrayBuffer(const ArrayBuffer&) = delete;
|
|
52
|
-
// ArrayBuffer cannot be moved
|
|
53
|
-
ArrayBuffer(ArrayBuffer&&) = delete;
|
|
54
|
-
|
|
55
|
-
~ArrayBuffer() {
|
|
83
|
+
~NativeArrayBuffer() {
|
|
56
84
|
if (_deleteFunc != nullptr) {
|
|
57
85
|
_deleteFunc(_data);
|
|
58
86
|
}
|
|
59
87
|
}
|
|
60
88
|
|
|
61
|
-
uint8_t* data()
|
|
89
|
+
uint8_t* data() override {
|
|
62
90
|
return _data;
|
|
63
91
|
}
|
|
64
92
|
|
|
65
|
-
size_t size() const
|
|
93
|
+
size_t size() const override {
|
|
66
94
|
return _size;
|
|
67
95
|
}
|
|
68
96
|
|
|
69
|
-
bool isOwner() const noexcept {
|
|
97
|
+
bool isOwner() const noexcept override {
|
|
70
98
|
return _deleteFunc != nullptr;
|
|
71
99
|
}
|
|
72
100
|
|
|
@@ -76,4 +104,68 @@ private:
|
|
|
76
104
|
DeleteFn _deleteFunc;
|
|
77
105
|
};
|
|
78
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Represents a JS-based `ArrayBuffer`.
|
|
109
|
+
*
|
|
110
|
+
* While it's underlying data might have been allocated on the native side (`NativeArrayBuffer`),
|
|
111
|
+
* we only have a JS reference to the `ArrayBuffer` object so it is considered a "borrowed"-resource.
|
|
112
|
+
*
|
|
113
|
+
* `data()` and `size()` can only be accessed synchronously on the JS Runtime Thread.
|
|
114
|
+
* If you want to access it elsewhere, copy the buffer first.
|
|
115
|
+
*
|
|
116
|
+
* If the JS ArrayBuffer (or it's JS Runtime) have already been deleted, `data()` returns `nullptr`.
|
|
117
|
+
*/
|
|
118
|
+
class JSArrayBuffer : public ArrayBuffer {
|
|
119
|
+
public:
|
|
120
|
+
explicit JSArrayBuffer(jsi::Runtime* runtime, OwningReference<jsi::ArrayBuffer> jsReference)
|
|
121
|
+
: ArrayBuffer(), _runtime(runtime), _jsReference(jsReference), _initialThreadId(std::this_thread::get_id()) {}
|
|
122
|
+
~JSArrayBuffer() {}
|
|
123
|
+
|
|
124
|
+
public:
|
|
125
|
+
/**
|
|
126
|
+
* Gets the data this `ArrayBuffer` points to, or `nullptr` if it has already been deleted.
|
|
127
|
+
*/
|
|
128
|
+
uint8_t* data() override {
|
|
129
|
+
if (_initialThreadId != std::this_thread::get_id()) [[unlikely]] {
|
|
130
|
+
throw std::runtime_error("`data()` can only be accessed synchronously on the JS Thread! "
|
|
131
|
+
"If you want to access it elsewhere, copy it first.");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
OwningLock<jsi::ArrayBuffer> lock = _jsReference.lock();
|
|
135
|
+
if (!_jsReference) [[unlikely]] {
|
|
136
|
+
// JS Part has been deleted - data is now nullptr.
|
|
137
|
+
return nullptr;
|
|
138
|
+
}
|
|
139
|
+
// JS Part is still alive - we can assume that the jsi::Runtime is safe to access here too.
|
|
140
|
+
return _jsReference->data(*_runtime);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Gets the size of the data this `ArrayBuffer` points to, or `0` if it has already been deleted.
|
|
145
|
+
*/
|
|
146
|
+
size_t size() const override {
|
|
147
|
+
if (_initialThreadId != std::this_thread::get_id()) [[unlikely]] {
|
|
148
|
+
throw std::runtime_error("`size()` can only be accessed synchronously on the JS Thread! "
|
|
149
|
+
"If you want to access it elsewhere, copy it first.");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
OwningLock<jsi::ArrayBuffer> lock = _jsReference.lock();
|
|
153
|
+
if (!_jsReference) [[unlikely]] {
|
|
154
|
+
// JS Part has been deleted - size is now 0.
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
// JS Part is still alive - we can assume that the jsi::Runtime is safe to access here too.
|
|
158
|
+
return _jsReference->size(*_runtime);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
bool isOwner() const noexcept override {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private:
|
|
166
|
+
jsi::Runtime* _runtime;
|
|
167
|
+
OwningReference<jsi::ArrayBuffer> _jsReference;
|
|
168
|
+
std::thread::id _initialThreadId;
|
|
169
|
+
};
|
|
170
|
+
|
|
79
171
|
} // namespace margelo::nitro
|
|
@@ -99,7 +99,7 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// 2. There is either no value in cache, or it's already dead. Create a new one
|
|
102
|
-
auto cache = JSICache
|
|
102
|
+
auto cache = JSICache::getOrCreateCache(runtime);
|
|
103
103
|
// 3. Create a new jsi::Object from this HybridObject/jsi::HostObject
|
|
104
104
|
std::shared_ptr<HybridObject> shared = shared_from_this();
|
|
105
105
|
jsi::Object object = jsi::Object::createFromHostObject(runtime, shared);
|
|
@@ -107,7 +107,7 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
|
|
|
107
107
|
size_t memorySize = getTotalExternalMemorySize();
|
|
108
108
|
object.setExternalMemoryPressure(runtime, memorySize);
|
|
109
109
|
// 5. Make it a global (weak) reference
|
|
110
|
-
auto global = cache.makeGlobal(std::move(object));
|
|
110
|
+
auto global = cache.makeGlobal<jsi::Object>(std::move(object));
|
|
111
111
|
_jsObjects[&runtime] = global.weak();
|
|
112
112
|
// 6. Return a jsi::Value copy again to the caller.
|
|
113
113
|
return jsi::Value(runtime, *global);
|
|
@@ -177,12 +177,12 @@ jsi::Value HybridObject::get(facebook::jsi::Runtime& runtime, const facebook::js
|
|
|
177
177
|
// it's a function. we now need to wrap it in a jsi::Function, store it in cache, then return it.
|
|
178
178
|
HybridFunction& hybridFunction = _methods.at(name);
|
|
179
179
|
// get (or create) a runtime-specific function cache
|
|
180
|
-
auto runtimeCache = JSICache
|
|
180
|
+
auto runtimeCache = JSICache::getOrCreateCache(runtime);
|
|
181
181
|
// create the jsi::Function
|
|
182
182
|
jsi::Function function = jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, name),
|
|
183
183
|
hybridFunction.parameterCount, hybridFunction.function);
|
|
184
184
|
// throw it into the cache for next time
|
|
185
|
-
OwningReference<jsi::Function> globalFunction = runtimeCache.makeGlobal(std::move(function));
|
|
185
|
+
OwningReference<jsi::Function> globalFunction = runtimeCache.makeGlobal<jsi::Function>(std::move(function));
|
|
186
186
|
functionCache[name] = globalFunction;
|
|
187
187
|
// copy the reference & return it to JS
|
|
188
188
|
return jsi::Value(runtime, *globalFunction);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JSICache.cpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 20.06.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#include "JSICache.hpp"
|
|
9
|
+
|
|
10
|
+
namespace margelo::nitro {
|
|
11
|
+
|
|
12
|
+
static constexpr auto CACHE_PROP_NAME = "__nitroModulesJSICache";
|
|
13
|
+
|
|
14
|
+
JSICache::~JSICache() {
|
|
15
|
+
Logger::log(TAG, "Destroying JSICache...");
|
|
16
|
+
std::unique_lock lock(_mutex);
|
|
17
|
+
|
|
18
|
+
for (auto& func : _cache) {
|
|
19
|
+
OwningReference<jsi::Object> owning = func.lock();
|
|
20
|
+
if (owning) {
|
|
21
|
+
// Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
|
|
22
|
+
owning.destroy();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
JSICacheReference JSICache::getOrCreateCache(jsi::Runtime& runtime) {
|
|
28
|
+
auto found = _globalCache.find(&runtime);
|
|
29
|
+
if (found != _globalCache.end()) {
|
|
30
|
+
// Fast path: get weak_ptr to JSICache from our global list.
|
|
31
|
+
std::weak_ptr<JSICache> weak = found->second;
|
|
32
|
+
std::shared_ptr<JSICache> strong = weak.lock();
|
|
33
|
+
if (strong) {
|
|
34
|
+
// It's still alive! Return it
|
|
35
|
+
return JSICacheReference(strong);
|
|
36
|
+
}
|
|
37
|
+
Logger::log(TAG, "JSICache was created, but it is no longer strong!");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Cache doesn't exist yet.
|
|
41
|
+
Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
|
|
42
|
+
// Create new cache
|
|
43
|
+
auto nativeState = std::make_shared<JSICache>(&runtime);
|
|
44
|
+
// Wrap it in a jsi::Value using NativeState
|
|
45
|
+
jsi::Object cache(runtime);
|
|
46
|
+
cache.setNativeState(runtime, nativeState);
|
|
47
|
+
// Inject it into the jsi::Runtime's global so it's memory is managed by it
|
|
48
|
+
runtime.global().setProperty(runtime, CACHE_PROP_NAME, std::move(cache));
|
|
49
|
+
// Add it to our map of caches
|
|
50
|
+
_globalCache[&runtime] = nativeState;
|
|
51
|
+
// Return it
|
|
52
|
+
return JSICacheReference(nativeState);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
} // namespace margelo::nitro
|
package/cpp/jsi/JSICache.hpp
CHANGED
|
@@ -21,73 +21,23 @@ namespace margelo::nitro {
|
|
|
21
21
|
|
|
22
22
|
using namespace facebook;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
template <typename T>
|
|
27
|
-
class JSICache;
|
|
28
|
-
|
|
29
|
-
template <typename T>
|
|
30
|
-
class JSICacheReference final {
|
|
31
|
-
public:
|
|
32
|
-
JSICacheReference() = delete;
|
|
33
|
-
JSICacheReference(const JSICacheReference&) = delete;
|
|
34
|
-
JSICacheReference(JSICacheReference&&) = delete;
|
|
35
|
-
|
|
36
|
-
~JSICacheReference() {
|
|
37
|
-
_strongCache->_mutex.unlock();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public:
|
|
41
|
-
/**
|
|
42
|
-
Creates a reference to a `jsi::Value` that can be stored in memory and accessed later.
|
|
43
|
-
The `jsi::Value` will be managed by the `jsi::Runtime`, if the `jsi::Runtime` gets the destroyed,
|
|
44
|
-
so will the `jsi::Value`.
|
|
45
|
-
|
|
46
|
-
To access the `jsi::Value`, try to `.lock()` the `weak_ptr`.
|
|
47
|
-
If it can be locked, it is still valid, otherwise the Runtime has already been deleted.
|
|
48
|
-
Do not hold the returned `shared_ptr` in memory, only use it in the calling function's scope.
|
|
49
|
-
Note: By design, this is not thread-safe, the returned `weak_ptr` must only be locked on the same thread as it was created on.
|
|
50
|
-
*/
|
|
51
|
-
OwningReference<T> makeGlobal(T&& value) {
|
|
52
|
-
auto owning = OwningReference<T>(new T(std::move(value)));
|
|
53
|
-
_strongCache->_cache.push_back(owning.weak());
|
|
54
|
-
return owning;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private:
|
|
58
|
-
explicit JSICacheReference(const std::shared_ptr<JSICache<T>>& cache) : _strongCache(cache) {
|
|
59
|
-
_strongCache->_mutex.lock();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private:
|
|
63
|
-
std::shared_ptr<JSICache<T>> _strongCache;
|
|
64
|
-
|
|
65
|
-
friend class JSICache<T>;
|
|
66
|
-
};
|
|
24
|
+
class JSICacheReference;
|
|
67
25
|
|
|
68
26
|
/**
|
|
69
|
-
* A `JSICache` can safely store `jsi::
|
|
27
|
+
* A `JSICache` can safely store `jsi::Object` instances (e.g. `jsi::Object` or
|
|
70
28
|
* `jsi::Function`) inside `OwningReference<T>`.
|
|
71
29
|
*
|
|
72
|
-
* `jsi::
|
|
73
|
-
* is deleted - even if there are still strong references to the `jsi::
|
|
30
|
+
* `jsi::Object`s are managed by a `jsi::Runtime`, and will be deleted if the `jsi::Runtime`
|
|
31
|
+
* is deleted - even if there are still strong references to the `jsi::Object`.
|
|
32
|
+
*
|
|
33
|
+
* To access a `OwningReference<jsi::Object>` safely, use `lock()` to get an `OwningLock<jsi::Object>`.
|
|
34
|
+
* This will allow you to access the `jsi::Object` as long as the `OwningLock` is alive,
|
|
35
|
+
* and `JSICache` will hold any garbage collection calls until the `OwningLock` is destroyed.
|
|
74
36
|
*/
|
|
75
|
-
template <typename T = jsi::Pointer>
|
|
76
37
|
class JSICache final : public jsi::NativeState {
|
|
77
38
|
public:
|
|
78
39
|
explicit JSICache(jsi::Runtime* runtime) : _runtime(runtime) {}
|
|
79
|
-
|
|
80
|
-
~JSICache() {
|
|
81
|
-
std::unique_lock lock(_mutex);
|
|
82
|
-
|
|
83
|
-
for (auto& func : _cache) {
|
|
84
|
-
OwningReference<T> owning = func.lock();
|
|
85
|
-
if (owning) {
|
|
86
|
-
// Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
|
|
87
|
-
owning.destroy();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
40
|
+
~JSICache();
|
|
91
41
|
|
|
92
42
|
public:
|
|
93
43
|
JSICache() = delete;
|
|
@@ -102,47 +52,61 @@ public:
|
|
|
102
52
|
Do not hold the returned `shared_ptr` in memory, only use it in the calling function's scope.
|
|
103
53
|
*/
|
|
104
54
|
[[nodiscard]]
|
|
105
|
-
static JSICacheReference
|
|
106
|
-
auto found = _globalCache.find(&runtime);
|
|
107
|
-
if (found != _globalCache.end()) {
|
|
108
|
-
// Fast path: get weak_ptr to JSICache from our global list.
|
|
109
|
-
std::weak_ptr<JSICache<T>> weak = found->second;
|
|
110
|
-
std::shared_ptr<JSICache<T>> strong = weak.lock();
|
|
111
|
-
if (strong) {
|
|
112
|
-
// It's still alive! Return it
|
|
113
|
-
return JSICacheReference<T>(strong);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Cache doesn't exist yet.
|
|
118
|
-
Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
|
|
119
|
-
// Create new cache
|
|
120
|
-
auto nativeState = std::make_shared<JSICache<T>>(&runtime);
|
|
121
|
-
// Wrap it in a jsi::Value using NativeState
|
|
122
|
-
jsi::Object cache(runtime);
|
|
123
|
-
cache.setNativeState(runtime, nativeState);
|
|
124
|
-
// Inject it into the jsi::Runtime's global so it's memory is managed by it
|
|
125
|
-
runtime.global().setProperty(runtime, CACHE_PROP_NAME, std::move(cache));
|
|
126
|
-
// Add it to our map of caches
|
|
127
|
-
_globalCache[&runtime] = nativeState;
|
|
128
|
-
// Return it
|
|
129
|
-
return JSICacheReference<T>(nativeState);
|
|
130
|
-
}
|
|
55
|
+
static JSICacheReference getOrCreateCache(jsi::Runtime& runtime);
|
|
131
56
|
|
|
132
|
-
public:
|
|
133
57
|
private:
|
|
134
|
-
friend class JSICacheReference
|
|
58
|
+
friend class JSICacheReference;
|
|
135
59
|
|
|
136
60
|
private:
|
|
137
61
|
jsi::Runtime* _runtime;
|
|
138
62
|
std::mutex _mutex;
|
|
139
|
-
std::vector<BorrowingReference<
|
|
63
|
+
std::vector<BorrowingReference<jsi::Object>> _cache;
|
|
140
64
|
|
|
141
65
|
private:
|
|
142
|
-
static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache
|
|
66
|
+
static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache>> _globalCache;
|
|
143
67
|
|
|
144
68
|
private:
|
|
145
69
|
static constexpr auto TAG = "JSICache";
|
|
146
70
|
};
|
|
147
71
|
|
|
72
|
+
class JSICacheReference final {
|
|
73
|
+
public:
|
|
74
|
+
JSICacheReference() = delete;
|
|
75
|
+
JSICacheReference(const JSICacheReference&) = delete;
|
|
76
|
+
JSICacheReference(JSICacheReference&&) = delete;
|
|
77
|
+
|
|
78
|
+
~JSICacheReference() {
|
|
79
|
+
_strongCache->_mutex.unlock();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public:
|
|
83
|
+
/**
|
|
84
|
+
* Creates a semi-strong reference to a `jsi::Object` that can be stored in memory and accessed later.
|
|
85
|
+
*
|
|
86
|
+
* The returned `OwningReference<T>` keeps the `T` semi-strong, and once it gets deleted, `T` will also be freed.
|
|
87
|
+
*
|
|
88
|
+
* Note: The JS Runtime can still delete `T` at any point (e.g. when it gets destroyed or reloaded),
|
|
89
|
+
* so any operations on `T` should be under an `OwningLock<T>`.
|
|
90
|
+
*
|
|
91
|
+
* To get an `OwningLock<T>` (which implies safe access to `T`), call `.locK()` on the `OwningReference<T>`.
|
|
92
|
+
* This will hold off any JS Runtime GC calls that will destroy `T` for as long as the `OwningLock<T>` is alive.
|
|
93
|
+
*/
|
|
94
|
+
template <typename T>
|
|
95
|
+
OwningReference<T> makeGlobal(T&& value) {
|
|
96
|
+
OwningReference<jsi::Object> owning(new T(std::move(value)));
|
|
97
|
+
_strongCache->_cache.push_back(owning.weak());
|
|
98
|
+
return owning.as<T>();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private:
|
|
102
|
+
explicit JSICacheReference(const std::shared_ptr<JSICache>& cache) : _strongCache(cache) {
|
|
103
|
+
_strongCache->_mutex.lock();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private:
|
|
107
|
+
std::shared_ptr<JSICache> _strongCache;
|
|
108
|
+
|
|
109
|
+
friend class JSICache;
|
|
110
|
+
};
|
|
111
|
+
|
|
148
112
|
} // namespace margelo::nitro
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
9
|
class ArrayBuffer;
|
|
10
|
+
class JSICache;
|
|
10
11
|
|
|
11
12
|
template <typename T, typename Enable>
|
|
12
13
|
struct JSIConverter;
|
|
@@ -15,25 +16,31 @@ struct JSIConverter;
|
|
|
15
16
|
#include "JSIConverter.hpp"
|
|
16
17
|
|
|
17
18
|
#include "ArrayBuffer.hpp"
|
|
19
|
+
#include "IsSharedPtrTo.hpp"
|
|
20
|
+
#include "JSICache.hpp"
|
|
18
21
|
#include <jsi/jsi.h>
|
|
19
22
|
#include <memory>
|
|
23
|
+
#include <type_traits>
|
|
20
24
|
|
|
21
25
|
namespace margelo::nitro {
|
|
22
26
|
|
|
23
27
|
using namespace facebook;
|
|
24
28
|
|
|
25
29
|
// MutableBuffer <> ArrayBuffer
|
|
26
|
-
template
|
|
27
|
-
struct JSIConverter<std::
|
|
30
|
+
template <typename T>
|
|
31
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer>>> {
|
|
28
32
|
static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
29
33
|
jsi::Object object = arg.asObject(runtime);
|
|
30
34
|
if (!object.isArrayBuffer(runtime)) [[unlikely]] {
|
|
31
35
|
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) + "\" is not an ArrayBuffer!");
|
|
32
36
|
}
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
|
|
38
|
+
JSICacheReference cache = JSICache::getOrCreateCache(runtime);
|
|
39
|
+
auto owningArrayBuffer = cache.makeGlobal<jsi::ArrayBuffer>(object.getArrayBuffer(runtime));
|
|
40
|
+
|
|
41
|
+
return std::make_shared<JSArrayBuffer>(&runtime, owningArrayBuffer);
|
|
35
42
|
}
|
|
36
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<jsi::MutableBuffer
|
|
43
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<jsi::MutableBuffer>& buffer) {
|
|
37
44
|
return jsi::ArrayBuffer(runtime, buffer);
|
|
38
45
|
}
|
|
39
46
|
};
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
|
-
template <typename T>
|
|
10
9
|
class JSICache;
|
|
11
10
|
|
|
12
11
|
template <typename T, typename Enable>
|
|
@@ -34,9 +33,9 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
34
33
|
|
|
35
34
|
static inline std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
36
35
|
// Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
|
|
37
|
-
auto cache = JSICache
|
|
36
|
+
auto cache = JSICache::getOrCreateCache(runtime);
|
|
38
37
|
jsi::Function function = arg.asObject(runtime).asFunction(runtime);
|
|
39
|
-
OwningReference<jsi::Function> sharedFunction = cache.makeGlobal(std::move(function));
|
|
38
|
+
OwningReference<jsi::Function> sharedFunction = cache.makeGlobal<jsi::Function>(std::move(function));
|
|
40
39
|
|
|
41
40
|
std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
42
41
|
std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
|
|
@@ -15,8 +15,7 @@ struct JSIConverter;
|
|
|
15
15
|
#include "JSIConverter.hpp"
|
|
16
16
|
|
|
17
17
|
#include "HybridObject.hpp"
|
|
18
|
-
#include "
|
|
19
|
-
#include "IsNativeState.hpp"
|
|
18
|
+
#include "IsSharedPtrTo.hpp"
|
|
20
19
|
#include "TypeInfo.hpp"
|
|
21
20
|
#include <jsi/jsi.h>
|
|
22
21
|
#include <memory>
|
|
@@ -30,7 +29,7 @@ using namespace facebook;
|
|
|
30
29
|
|
|
31
30
|
// HybridObject <> {}
|
|
32
31
|
template <typename T>
|
|
33
|
-
struct JSIConverter<T, std::enable_if_t<
|
|
32
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::HostObject>>> {
|
|
34
33
|
using TPointee = typename T::element_type;
|
|
35
34
|
|
|
36
35
|
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
@@ -45,6 +44,10 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_host_object_v<T>>> {
|
|
|
45
44
|
#endif
|
|
46
45
|
jsi::Object object = arg.asObject(runtime);
|
|
47
46
|
#if DO_NULL_CHECKS
|
|
47
|
+
if (!object.isHostObject(runtime)) [[unlikely]] {
|
|
48
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
49
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not a HostObject!"));
|
|
50
|
+
}
|
|
48
51
|
if (!object.isHostObject<TPointee>(runtime)) [[unlikely]] {
|
|
49
52
|
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
50
53
|
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different HostObject<T>!"));
|
|
@@ -78,7 +81,7 @@ private:
|
|
|
78
81
|
|
|
79
82
|
// NativeState <> {}
|
|
80
83
|
template <typename T>
|
|
81
|
-
struct JSIConverter<T, std::enable_if_t<
|
|
84
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> {
|
|
82
85
|
using TPointee = typename T::element_type;
|
|
83
86
|
|
|
84
87
|
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
@@ -93,6 +96,10 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_native_state_v<T>>> {
|
|
|
93
96
|
#endif
|
|
94
97
|
jsi::Object object = arg.asObject(runtime);
|
|
95
98
|
#if DO_NULL_CHECKS
|
|
99
|
+
if (!object.hasNativeState(runtime)) [[unlikely]] {
|
|
100
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
101
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not a NativeState!"));
|
|
102
|
+
}
|
|
96
103
|
if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
|
|
97
104
|
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
98
105
|
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different NativeState<T>!"));
|
|
@@ -13,9 +13,9 @@ template <typename T, typename Enable>
|
|
|
13
13
|
struct JSIConverter;
|
|
14
14
|
} // namespace margelo::nitro
|
|
15
15
|
|
|
16
|
-
#include "JSIConverter.hpp"
|
|
17
|
-
#include "JSIConverter+Vector.hpp"
|
|
18
16
|
#include "JSIConverter+UnorderedMap.hpp"
|
|
17
|
+
#include "JSIConverter+Vector.hpp"
|
|
18
|
+
#include "JSIConverter.hpp"
|
|
19
19
|
|
|
20
20
|
#include "AnyMap.hpp"
|
|
21
21
|
#include "IsInPack.hpp"
|
package/cpp/jsi/Promise.cpp
CHANGED
|
@@ -8,9 +8,9 @@ namespace margelo::nitro {
|
|
|
8
8
|
using namespace facebook;
|
|
9
9
|
|
|
10
10
|
Promise::Promise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter) {
|
|
11
|
-
auto functionCache = JSICache
|
|
12
|
-
_resolver = functionCache.makeGlobal(std::move(resolver));
|
|
13
|
-
_rejecter = functionCache.makeGlobal(std::move(rejecter));
|
|
11
|
+
auto functionCache = JSICache::getOrCreateCache(runtime);
|
|
12
|
+
_resolver = functionCache.makeGlobal<jsi::Function>(std::move(resolver));
|
|
13
|
+
_rejecter = functionCache.makeGlobal<jsi::Function>(std::move(rejecter));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
jsi::Value Promise::createPromise(jsi::Runtime& runtime, RunPromise&& run) {
|
|
@@ -35,6 +35,8 @@ jsi::Value Promise::createPromise(jsi::Runtime& runtime, RunPromise&& run) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
void Promise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
|
|
38
|
+
OwningLock<jsi::Function> lock = _resolver.lock();
|
|
39
|
+
|
|
38
40
|
if (!_resolver) {
|
|
39
41
|
Logger::log(TAG, "Promise resolver function has already been deleted! Ignoring call..");
|
|
40
42
|
return;
|
|
@@ -43,6 +45,8 @@ void Promise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
void Promise::reject(jsi::Runtime& runtime, std::string message) {
|
|
48
|
+
OwningLock<jsi::Function> lock = _rejecter.lock();
|
|
49
|
+
|
|
46
50
|
if (!_rejecter) {
|
|
47
51
|
Logger::log(TAG, "Promise rejecter function has already been deleted! Ignoring call..");
|
|
48
52
|
return;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//
|
|
2
|
+
// IsSharedPtrTo.hpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 21.06.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <jsi/jsi.h>
|
|
11
|
+
#include <type_traits>
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro {
|
|
14
|
+
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
|
|
17
|
+
// Returns whether the given type T is a shared_ptr to type P.
|
|
18
|
+
template <typename T, typename P>
|
|
19
|
+
struct is_shared_ptr_to : std::false_type {};
|
|
20
|
+
|
|
21
|
+
template <typename T, typename P>
|
|
22
|
+
struct is_shared_ptr_to<std::shared_ptr<T>, P> : std::is_base_of<typename std::remove_cv<typename std::remove_reference<P>::type>::type,
|
|
23
|
+
typename std::remove_cv<typename std::remove_reference<T>::type>::type> {};
|
|
24
|
+
|
|
25
|
+
template <typename T, typename P>
|
|
26
|
+
constexpr bool is_shared_ptr_to_v = is_shared_ptr_to<T, P>::value;
|
|
27
|
+
|
|
28
|
+
} // namespace margelo::nitro
|
|
@@ -76,12 +76,25 @@ public:
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
private:
|
|
79
|
+
// BorrowingReference<T> -> OwningReference<T> Lock-constructor
|
|
79
80
|
OwningReference(const BorrowingReference<T>& ref)
|
|
80
81
|
: _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
|
|
81
82
|
_mutex(ref._mutex) {
|
|
82
83
|
(*_strongRefCount)++;
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
private:
|
|
87
|
+
// OwningReference<C> -> OwningReference<T> Cast-constructor
|
|
88
|
+
template <typename OldT>
|
|
89
|
+
OwningReference(T* value, const OwningReference<OldT>& originalRef)
|
|
90
|
+
: _value(value), _isDeleted(originalRef._isDeleted), _strongRefCount(originalRef._strongRefCount),
|
|
91
|
+
_weakRefCount(originalRef._weakRefCount), _mutex(originalRef._mutex) {
|
|
92
|
+
(*_strongRefCount)++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
template <typename C>
|
|
96
|
+
friend class OwningReference;
|
|
97
|
+
|
|
85
98
|
public:
|
|
86
99
|
~OwningReference() {
|
|
87
100
|
if (_strongRefCount == nullptr) {
|
|
@@ -94,6 +107,15 @@ public:
|
|
|
94
107
|
maybeDestroy();
|
|
95
108
|
}
|
|
96
109
|
|
|
110
|
+
public:
|
|
111
|
+
/**
|
|
112
|
+
Casts this `OwningReference<T>` to a `OwningReference<C>`.
|
|
113
|
+
*/
|
|
114
|
+
template <typename C>
|
|
115
|
+
OwningReference<C> as() {
|
|
116
|
+
return OwningReference<C>(static_cast<C*>(_value), *this);
|
|
117
|
+
}
|
|
118
|
+
|
|
97
119
|
public:
|
|
98
120
|
/**
|
|
99
121
|
Creates an `OwningLock<T>` for the given `OwningReference<T>` to guarantee safe
|
package/package.json
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// BorrowingReference.hpp
|
|
3
|
-
// NitroModules
|
|
4
|
-
//
|
|
5
|
-
// Created by Marc Rousavy on 21.06.24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
|
-
#include <jsi/jsi.h>
|
|
11
|
-
#include <type_traits>
|
|
12
|
-
|
|
13
|
-
namespace margelo::nitro {
|
|
14
|
-
|
|
15
|
-
using namespace facebook;
|
|
16
|
-
|
|
17
|
-
// Returns whether the given type `T` is a `shared_ptr` to a `HostObject`
|
|
18
|
-
template <typename T>
|
|
19
|
-
struct is_shared_ptr_to_host_object : std::false_type {};
|
|
20
|
-
|
|
21
|
-
template <typename T>
|
|
22
|
-
struct is_shared_ptr_to_host_object<std::shared_ptr<T>> : std::is_base_of<jsi::HostObject, T> {};
|
|
23
|
-
|
|
24
|
-
template <typename T>
|
|
25
|
-
inline constexpr bool is_shared_ptr_to_host_object_v = is_shared_ptr_to_host_object<std::remove_reference_t<T>>::value;
|
|
26
|
-
|
|
27
|
-
} // namespace margelo::nitro
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// BorrowingReference.hpp
|
|
3
|
-
// NitroModules
|
|
4
|
-
//
|
|
5
|
-
// Created by Marc Rousavy on 21.06.24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
|
-
#include <jsi/jsi.h>
|
|
11
|
-
#include <type_traits>
|
|
12
|
-
|
|
13
|
-
namespace margelo::nitro {
|
|
14
|
-
|
|
15
|
-
using namespace facebook;
|
|
16
|
-
|
|
17
|
-
// Returns whether the given type `T` is a `shared_ptr` to a `NativeStaet`
|
|
18
|
-
template <typename T>
|
|
19
|
-
struct is_shared_ptr_to_native_state : std::false_type {};
|
|
20
|
-
|
|
21
|
-
template <typename T>
|
|
22
|
-
struct is_shared_ptr_to_native_state<std::shared_ptr<T>> : std::is_base_of<jsi::NativeState, T> {};
|
|
23
|
-
|
|
24
|
-
template <typename T>
|
|
25
|
-
inline constexpr bool is_shared_ptr_to_native_state_v = is_shared_ptr_to_native_state<std::remove_reference_t<T>>::value;
|
|
26
|
-
|
|
27
|
-
} // namespace margelo::nitro
|