react-native-nitro-modules 0.1.5 → 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.
@@ -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<jsi::Object>::getOrCreateCache(runtime);
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<jsi::Function>::getOrCreateCache(runtime);
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
@@ -21,73 +21,23 @@ namespace margelo::nitro {
21
21
 
22
22
  using namespace facebook;
23
23
 
24
- static constexpr auto CACHE_PROP_NAME = "__nitroModulesJSICache";
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::Pointer` instances (e.g. `jsi::Object` or
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::Pointer`s are managed by a `jsi::Runtime`, and will be deleted if the `jsi::Runtime`
73
- * is deleted - even if there are still strong references to the `jsi::Pointer`.
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<T> getOrCreateCache(jsi::Runtime& runtime) {
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<T>;
58
+ friend class JSICacheReference;
135
59
 
136
60
  private:
137
61
  jsi::Runtime* _runtime;
138
62
  std::mutex _mutex;
139
- std::vector<BorrowingReference<T>> _cache;
63
+ std::vector<BorrowingReference<jsi::Object>> _cache;
140
64
 
141
65
  private:
142
- static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache<T>>> _globalCache;
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,9 +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
-
11
- template <typename T>
12
- struct JSICache;
10
+ class JSICache;
13
11
 
14
12
  template <typename T, typename Enable>
15
13
  struct JSIConverter;
@@ -37,12 +35,12 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer
37
35
  throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) + "\" is not an ArrayBuffer!");
38
36
  }
39
37
 
40
- JSICacheReference<jsi::ArrayBuffer> cache = JSICache<jsi::ArrayBuffer>::getOrCreateCache(runtime);
41
- auto owningArrayBuffer = cache.makeGlobal(object.getArrayBuffer(runtime));
38
+ JSICacheReference cache = JSICache::getOrCreateCache(runtime);
39
+ auto owningArrayBuffer = cache.makeGlobal<jsi::ArrayBuffer>(object.getArrayBuffer(runtime));
42
40
 
43
41
  return std::make_shared<JSArrayBuffer>(&runtime, owningArrayBuffer);
44
42
  }
45
- static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<jsi::MutableBuffer> buffer) {
43
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<jsi::MutableBuffer>& buffer) {
46
44
  return jsi::ArrayBuffer(runtime, buffer);
47
45
  }
48
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<jsi::Function>::getOrCreateCache(runtime);
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;
@@ -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<jsi::Function>::getOrCreateCache(runtime);
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;
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-modules",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",