react-native-nitro-modules 0.22.0 → 0.23.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.
Files changed (48) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/gradle.properties +1 -1
  3. package/android/src/main/java/com/margelo/nitro/views/HybridView.kt +11 -0
  4. package/cpp/core/ArrayBuffer.cpp +2 -2
  5. package/cpp/core/ArrayBuffer.hpp +3 -3
  6. package/cpp/core/HybridFunction.hpp +3 -0
  7. package/cpp/core/HybridObject.hpp +1 -1
  8. package/cpp/core/Promise.hpp +3 -3
  9. package/cpp/jsi/JSICache.cpp +4 -4
  10. package/cpp/jsi/JSICache.hpp +18 -18
  11. package/cpp/jsi/JSIConverter+Function.hpp +3 -2
  12. package/cpp/prototype/HybridObjectPrototype.cpp +2 -2
  13. package/cpp/prototype/HybridObjectPrototype.hpp +2 -2
  14. package/cpp/registry/HybridObjectRegistry.cpp +3 -0
  15. package/cpp/utils/BorrowingReference.hpp +161 -54
  16. package/cpp/utils/NitroDefines.hpp +1 -11
  17. package/cpp/utils/OwningLock.hpp +14 -14
  18. package/cpp/utils/ReferenceState.hpp +40 -0
  19. package/cpp/utils/WeakReference+Owning.hpp +33 -0
  20. package/cpp/utils/WeakReference.hpp +102 -0
  21. package/cpp/views/CachedProp.hpp +3 -3
  22. package/ios/core/AnyMapHolder.swift +8 -8
  23. package/ios/turbomodule/NativeNitroModules+NewArch.mm +1 -1
  24. package/lib/commonjs/index.js +11 -0
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/commonjs/views/HybridView.js +0 -11
  27. package/lib/commonjs/views/HybridView.js.map +1 -1
  28. package/lib/commonjs/views/getHostComponent.js +20 -1
  29. package/lib/commonjs/views/getHostComponent.js.map +1 -1
  30. package/lib/module/index.js +1 -0
  31. package/lib/module/index.js.map +1 -1
  32. package/lib/module/views/HybridView.js +0 -26
  33. package/lib/module/views/HybridView.js.map +1 -1
  34. package/lib/module/views/getHostComponent.js +21 -1
  35. package/lib/module/views/getHostComponent.js.map +1 -1
  36. package/lib/tsconfig.build.tsbuildinfo +1 -1
  37. package/lib/typescript/index.d.ts +1 -0
  38. package/lib/typescript/index.d.ts.map +1 -1
  39. package/lib/typescript/views/HybridView.d.ts +89 -16
  40. package/lib/typescript/views/HybridView.d.ts.map +1 -1
  41. package/lib/typescript/views/getHostComponent.d.ts +46 -3
  42. package/lib/typescript/views/getHostComponent.d.ts.map +1 -1
  43. package/package.json +4 -4
  44. package/src/index.ts +2 -0
  45. package/src/views/HybridView.ts +96 -18
  46. package/src/views/getHostComponent.ts +67 -5
  47. package/cpp/utils/BorrowingReference+Owning.hpp +0 -36
  48. package/cpp/utils/OwningReference.hpp +0 -250
@@ -7,7 +7,7 @@ buildscript {
7
7
  }
8
8
 
9
9
  dependencies {
10
- classpath "com.android.tools.build:gradle:8.8.0"
10
+ classpath "com.android.tools.build:gradle:8.8.1"
11
11
  }
12
12
  }
13
13
 
@@ -1,5 +1,5 @@
1
1
  Nitro_kotlinVersion=2.0.21
2
2
  Nitro_minSdkVersion=23
3
- Nitro_targetSdkVersion=34
3
+ Nitro_targetSdkVersion=35
4
4
  Nitro_compileSdkVersion=34
5
5
  Nitro_ndkVersion=27.1.12297006
@@ -20,4 +20,15 @@ abstract class HybridView: HybridObject() {
20
20
  @get:DoNotStrip
21
21
  @get:Keep
22
22
  abstract val view: View
23
+
24
+ /**
25
+ * Called right before updating props.
26
+ * React props are updated in a single batch/transaction.
27
+ */
28
+ open fun beforeUpdate() { /* noop */ }
29
+ /**
30
+ * Called right after updating props.
31
+ * React props are updated in a single batch/transaction.
32
+ */
33
+ open fun afterUpdate() { /* noop */ }
23
34
  }
@@ -6,7 +6,7 @@
6
6
  //
7
7
 
8
8
  #include "ArrayBuffer.hpp"
9
- #include "OwningReference.hpp"
9
+ #include "BorrowingReference.hpp"
10
10
  #include <functional>
11
11
  #include <jsi/jsi.h>
12
12
  #include <thread>
@@ -61,7 +61,7 @@ bool NativeArrayBuffer::isOwner() const noexcept {
61
61
 
62
62
  // 3. JSArrayBuffer
63
63
 
64
- JSArrayBuffer::JSArrayBuffer(jsi::Runtime* runtime, OwningReference<jsi::ArrayBuffer> jsReference)
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() {}
@@ -7,7 +7,7 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include "OwningReference.hpp"
10
+ #include "BorrowingReference.hpp"
11
11
  #include <jsi/jsi.h>
12
12
  #include <thread>
13
13
  #include <vector>
@@ -122,7 +122,7 @@ private:
122
122
  */
123
123
  class JSArrayBuffer final : public ArrayBuffer {
124
124
  public:
125
- explicit JSArrayBuffer(jsi::Runtime* runtime, OwningReference<jsi::ArrayBuffer> jsReference);
125
+ explicit JSArrayBuffer(jsi::Runtime* runtime, BorrowingReference<jsi::ArrayBuffer> jsReference);
126
126
  ~JSArrayBuffer();
127
127
 
128
128
  public:
@@ -141,7 +141,7 @@ public:
141
141
 
142
142
  private:
143
143
  jsi::Runtime* _runtime;
144
- OwningReference<jsi::ArrayBuffer> _jsReference;
144
+ BorrowingReference<jsi::ArrayBuffer> _jsReference;
145
145
  std::thread::id _initialThreadId;
146
146
  };
147
147
 
@@ -109,12 +109,15 @@ public:
109
109
  std::string message = exception.what();
110
110
  throw jsi::JSError(runtime, funcName + ": " + message);
111
111
  #ifdef ANDROID
112
+ #pragma clang diagnostic push
113
+ #pragma clang diagnostic ignored "-Wexceptions"
112
114
  // Workaround for https://github.com/mrousavy/nitro/issues/382
113
115
  } catch (const std::runtime_error& exception) {
114
116
  // Some exception was thrown - add method name information and re-throw as `JSError`.
115
117
  std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
116
118
  std::string message = exception.what();
117
119
  throw jsi::JSError(runtime, funcName + ": " + message);
120
+ #pragma clang diagnostic pop
118
121
  #endif
119
122
  } catch (...) {
120
123
  // Some unknown exception was thrown - add method name information and re-throw as `JSError`.
@@ -136,7 +136,7 @@ protected:
136
136
  private:
137
137
  static constexpr auto TAG = "HybridObject";
138
138
  const char* _name = TAG;
139
- std::unordered_map<jsi::Runtime*, OwningReference<jsi::WeakObject>> _objectCache;
139
+ std::unordered_map<jsi::Runtime*, BorrowingReference<jsi::WeakObject>> _objectCache;
140
140
  };
141
141
 
142
142
  } // namespace margelo::nitro
@@ -30,7 +30,7 @@ public:
30
30
  Promise(const Promise&) = delete;
31
31
 
32
32
  private:
33
- Promise() {}
33
+ Promise() = default;
34
34
 
35
35
  public:
36
36
  ~Promise() {
@@ -264,13 +264,13 @@ public:
264
264
  Promise(const Promise&) = delete;
265
265
 
266
266
  private:
267
- Promise() {}
267
+ Promise() = default;
268
268
 
269
269
  public:
270
270
  ~Promise() {
271
271
  if (isPending()) [[unlikely]] {
272
272
  std::runtime_error error("Timeouted: Promise<void> was destroyed!");
273
- reject(std::make_exception_ptr(std::move(error)));
273
+ reject(std::make_exception_ptr(error));
274
274
  }
275
275
  }
276
276
 
@@ -14,12 +14,12 @@ namespace margelo::nitro {
14
14
  static constexpr auto CACHE_PROP_NAME = "__nitroModulesJSICache";
15
15
 
16
16
  template <typename T>
17
- inline void destroyReferences(const std::vector<BorrowingReference<T>>& references) {
17
+ inline void destroyReferences(const std::vector<WeakReference<T>>& references) {
18
18
  for (auto& func : references) {
19
- OwningReference<T> owning = func.lock();
20
- if (owning) {
19
+ BorrowingReference<T> reference = func.lock();
20
+ if (reference) {
21
21
  // Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
22
- owning.destroy();
22
+ reference.destroy();
23
23
  }
24
24
  }
25
25
  }
@@ -9,7 +9,7 @@
9
9
 
10
10
  #include "BorrowingReference.hpp"
11
11
  #include "NitroLogger.hpp"
12
- #include "OwningReference.hpp"
12
+ #include "WeakReference.hpp"
13
13
  #include <jsi/jsi.h>
14
14
  #include <memory>
15
15
  #include <mutex>
@@ -24,12 +24,12 @@ class JSICacheReference;
24
24
 
25
25
  /**
26
26
  * A `JSICache` can safely store `jsi::Value` instances (e.g. `jsi::Object` or
27
- * `jsi::Function`) inside `OwningReference<T>`.
27
+ * `jsi::Function`) inside `BorrowingReference<T>`.
28
28
  *
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 `OwningReference<jsi::Value>` safely, use `lock()` to get an `OwningLock<jsi::Value>`.
32
+ * To access a `BorrowingReference<jsi::Value>` safely, use `lock()` to get an `OwningLock<jsi::Value>`.
33
33
  * This will allow you to access the `jsi::Value` as long as the `OwningLock` is alive,
34
34
  * and `JSICache` will hold any garbage collection calls until the `OwningLock` is destroyed.
35
35
  */
@@ -59,11 +59,11 @@ private:
59
59
 
60
60
  private:
61
61
  std::mutex _mutex;
62
- std::vector<BorrowingReference<jsi::Value>> _valueCache;
63
- std::vector<BorrowingReference<jsi::Object>> _objectCache;
64
- std::vector<BorrowingReference<jsi::Function>> _functionCache;
65
- std::vector<BorrowingReference<jsi::WeakObject>> _weakObjectCache;
66
- std::vector<BorrowingReference<jsi::ArrayBuffer>> _arrayBufferCache;
62
+ std::vector<WeakReference<jsi::Value>> _valueCache;
63
+ std::vector<WeakReference<jsi::Object>> _objectCache;
64
+ std::vector<WeakReference<jsi::Function>> _functionCache;
65
+ std::vector<WeakReference<jsi::WeakObject>> _weakObjectCache;
66
+ std::vector<WeakReference<jsi::ArrayBuffer>> _arrayBufferCache;
67
67
 
68
68
  private:
69
69
  static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache>> _globalCache;
@@ -83,28 +83,28 @@ public:
83
83
  }
84
84
 
85
85
  public:
86
- OwningReference<jsi::Value> makeShared(jsi::Value&& value) {
87
- OwningReference<jsi::Value> owning(new jsi::Value(std::move(value)));
86
+ BorrowingReference<jsi::Value> makeShared(jsi::Value&& value) {
87
+ BorrowingReference<jsi::Value> owning(new jsi::Value(std::move(value)));
88
88
  _strongCache->_valueCache.push_back(owning.weak());
89
89
  return owning;
90
90
  }
91
- OwningReference<jsi::Object> makeShared(jsi::Object&& value) {
92
- OwningReference<jsi::Object> owning(new jsi::Object(std::move(value)));
91
+ BorrowingReference<jsi::Object> makeShared(jsi::Object&& value) {
92
+ BorrowingReference<jsi::Object> owning(new jsi::Object(std::move(value)));
93
93
  _strongCache->_objectCache.push_back(owning.weak());
94
94
  return owning;
95
95
  }
96
- OwningReference<jsi::Function> makeShared(jsi::Function&& value) {
97
- OwningReference<jsi::Function> owning(new jsi::Function(std::move(value)));
96
+ BorrowingReference<jsi::Function> makeShared(jsi::Function&& value) {
97
+ BorrowingReference<jsi::Function> owning(new jsi::Function(std::move(value)));
98
98
  _strongCache->_functionCache.push_back(owning.weak());
99
99
  return owning;
100
100
  }
101
- OwningReference<jsi::WeakObject> makeShared(jsi::WeakObject&& value) {
102
- OwningReference<jsi::WeakObject> owning(new jsi::WeakObject(std::move(value)));
101
+ BorrowingReference<jsi::WeakObject> makeShared(jsi::WeakObject&& value) {
102
+ BorrowingReference<jsi::WeakObject> owning(new jsi::WeakObject(std::move(value)));
103
103
  _strongCache->_weakObjectCache.push_back(owning.weak());
104
104
  return owning;
105
105
  }
106
- OwningReference<jsi::ArrayBuffer> makeShared(jsi::ArrayBuffer&& value) {
107
- OwningReference<jsi::ArrayBuffer> owning(new jsi::ArrayBuffer(std::move(value)));
106
+ BorrowingReference<jsi::ArrayBuffer> makeShared(jsi::ArrayBuffer&& value) {
107
+ BorrowingReference<jsi::ArrayBuffer> owning(new jsi::ArrayBuffer(std::move(value)));
108
108
  _strongCache->_arrayBufferCache.push_back(owning.weak());
109
109
  return owning;
110
110
  }
@@ -34,7 +34,7 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
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
- OwningReference<jsi::Function> sharedFunction = cache.makeShared(std::move(function));
37
+ BorrowingReference<jsi::Function> sharedFunction = cache.makeShared(std::move(function));
38
38
 
39
39
  std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
40
40
  std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
@@ -94,7 +94,8 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
94
94
  }
95
95
 
96
96
  private:
97
- static inline ResultingType callJSFunction(jsi::Runtime& runtime, const OwningReference<jsi::Function>& function, const Args&... args) {
97
+ static inline ResultingType callJSFunction(jsi::Runtime& runtime, const BorrowingReference<jsi::Function>& function,
98
+ const Args&... args) {
98
99
  if (!function) {
99
100
  if constexpr (std::is_void_v<ResultingType>) {
100
101
  // runtime has already been deleted. since this returns void, we can just ignore it being deleted.
@@ -26,7 +26,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
26
26
  auto& prototypeCache = _prototypeCache[&runtime];
27
27
  auto cachedPrototype = prototypeCache.find(prototype->getNativeInstanceId());
28
28
  if (cachedPrototype != prototypeCache.end()) {
29
- const OwningReference<jsi::Object>& cachedObject = cachedPrototype->second;
29
+ const BorrowingReference<jsi::Object>& cachedObject = cachedPrototype->second;
30
30
  return jsi::Value(runtime, *cachedObject).getObject(runtime);
31
31
  }
32
32
 
@@ -68,7 +68,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
68
68
 
69
69
  // 6. Throw it into our cache so the next lookup can be cached and therefore faster
70
70
  JSICacheReference jsiCache = JSICache::getOrCreateCache(runtime);
71
- OwningReference<jsi::Object> cachedObject = jsiCache.makeShared(std::move(object));
71
+ BorrowingReference<jsi::Object> cachedObject = jsiCache.makeShared(std::move(object));
72
72
  prototypeCache.emplace(prototype->getNativeInstanceId(), cachedObject);
73
73
 
74
74
  // 7. In DEBUG, add a __type info to the prototype object.
@@ -7,8 +7,8 @@
7
7
 
8
8
  #pragma once
9
9
 
10
+ #include "BorrowingReference.hpp"
10
11
  #include "HybridFunction.hpp"
11
- #include "OwningReference.hpp"
12
12
  #include "Prototype.hpp"
13
13
  #include "PrototypeChain.hpp"
14
14
  #include <functional>
@@ -46,7 +46,7 @@ public:
46
46
 
47
47
  private:
48
48
  static jsi::Value createPrototype(jsi::Runtime& runtime, const std::shared_ptr<Prototype>& prototype);
49
- using PrototypeCache = std::unordered_map<NativeInstanceId, OwningReference<jsi::Object>>;
49
+ using PrototypeCache = std::unordered_map<NativeInstanceId, BorrowingReference<jsi::Object>>;
50
50
  static std::unordered_map<jsi::Runtime*, PrototypeCache> _prototypeCache;
51
51
 
52
52
  protected:
@@ -32,6 +32,9 @@ std::vector<std::string> HybridObjectRegistry::getAllHybridObjectNames() {
32
32
 
33
33
  std::string HybridObjectRegistry::getAllRegisteredHybridObjectNamesToString() {
34
34
  std::vector<std::string> names = getAllHybridObjectNames();
35
+ if (names.empty()) {
36
+ return "";
37
+ }
35
38
  return std::accumulate(std::next(names.begin()), names.end(), names[0], [](std::string a, std::string b) { return a + ", " + b; });
36
39
  }
37
40
 
@@ -1,119 +1,226 @@
1
1
  //
2
2
  // BorrowingReference.hpp
3
- // NitroModules
3
+ // react-native-nitro
4
4
  //
5
- // Created by Marc Rousavy on 21.06.24.
5
+ // Created by Marc Rousavy on 23.06.24.
6
6
  //
7
7
 
8
8
  #pragma once
9
9
 
10
+ #include "NitroDefines.hpp"
11
+ #include "OwningLock.hpp"
12
+ #include "ReferenceState.hpp"
13
+ #include "WeakReference.hpp"
10
14
  #include <atomic>
11
15
  #include <cstddef>
12
16
  #include <mutex>
13
17
 
14
18
  namespace margelo::nitro {
15
19
 
16
- // forward-declaration to avoid duplicate symbols
17
- template <typename T>
18
- class OwningReference;
19
-
20
20
  /**
21
- A `BorrowingReference<T>` is a weak reference to a pointer created by `OwningReference<T>`.
22
- It can be locked to gain a strong `OwningReference<T>` again if it has not been deleted yet.
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.
23
34
  */
24
35
  template <typename T>
25
36
  class BorrowingReference final {
26
- private:
27
- explicit BorrowingReference(const OwningReference<T>& ref);
28
-
29
37
  public:
30
- BorrowingReference() : _value(nullptr), _isDeleted(nullptr), _strongRefCount(nullptr), _weakRefCount(nullptr), _mutex(nullptr) {}
38
+ BorrowingReference() : _value(nullptr), _state(nullptr) {}
31
39
 
32
- BorrowingReference(const BorrowingReference& ref)
33
- : _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
34
- _mutex(ref._mutex) {
35
- if (_weakRefCount != nullptr) {
40
+ explicit BorrowingReference(T* value) : _value(value), _state(new ReferenceState()) {}
41
+
42
+ BorrowingReference(const BorrowingReference& ref) : _value(ref._value), _state(ref._state) {
43
+ if (_state != nullptr) {
36
44
  // increment ref count after copy
37
- (*_weakRefCount)++;
45
+ _state->strongRefCount++;
38
46
  }
39
47
  }
40
48
 
41
- BorrowingReference(BorrowingReference&& ref)
42
- : _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
43
- _mutex(ref._mutex) {
49
+ BorrowingReference(BorrowingReference&& ref) : _value(ref._value), _state(ref._state) {
44
50
  ref._value = nullptr;
45
- ref._isDeleted = nullptr;
46
- ref._strongRefCount = nullptr;
47
- ref._weakRefCount = nullptr;
51
+ ref._state = nullptr;
48
52
  }
49
53
 
50
54
  BorrowingReference& operator=(const BorrowingReference& ref) {
51
55
  if (this == &ref)
52
56
  return *this;
53
57
 
54
- if (_weakRefCount != nullptr) {
58
+ if (_state != nullptr) {
55
59
  // destroy previous pointer
56
- (*_weakRefCount)--;
57
- maybeDestroy();
60
+ bool shouldDestroy = _state->decrementStrongRefCount();
61
+ if (shouldDestroy) {
62
+ forceDestroyValue();
63
+ }
64
+ maybeDestroyState();
58
65
  }
59
66
 
60
67
  _value = ref._value;
61
- _isDeleted = ref._isDeleted;
62
- _strongRefCount = ref._strongRefCount;
63
- _weakRefCount = ref._weakRefCount;
64
- _mutex = ref._mutex;
65
- if (_weakRefCount != nullptr) {
68
+ _state = ref._state;
69
+ if (_state != nullptr) {
66
70
  // increment new pointer
67
- (*_weakRefCount)++;
71
+ _state->strongRefCount++;
68
72
  }
69
73
 
70
74
  return *this;
71
75
  }
72
76
 
77
+ private:
78
+ // WeakReference<T> -> BorrowingReference<T> Lock-constructor
79
+ BorrowingReference(const WeakReference<T>& ref) : _value(ref._value), _state(ref._state) {
80
+ _state->strongRefCount++;
81
+ }
82
+
83
+ private:
84
+ // BorrowingReference<C> -> BorrowingReference<T> Cast-constructor
85
+ template <typename OldT>
86
+ BorrowingReference(T* value, const BorrowingReference<OldT>& originalRef) : _value(value), _state(originalRef._state) {
87
+ _state->strongRefCount++;
88
+ }
89
+
90
+ template <typename C>
91
+ friend class BorrowingReference;
92
+
93
+ public:
73
94
  ~BorrowingReference() {
74
- if (_weakRefCount == nullptr) {
95
+ if (_state == nullptr) {
75
96
  // we are just a dangling nullptr.
76
97
  return;
77
98
  }
78
99
 
79
- (*_weakRefCount)--;
80
- maybeDestroy();
100
+ // decrement strong ref count on destroy
101
+ bool shouldDestroy = _state->decrementStrongRefCount();
102
+ if (shouldDestroy) {
103
+ forceDestroyValue();
104
+ }
105
+ maybeDestroyState();
106
+ }
107
+
108
+ public:
109
+ /**
110
+ Casts this `BorrowingReference<T>` to a `BorrowingReference<C>`.
111
+ */
112
+ template <typename C>
113
+ BorrowingReference<C> as() {
114
+ return BorrowingReference<C>(static_cast<C*>(_value), *this);
115
+ }
116
+
117
+ public:
118
+ /**
119
+ Creates an `OwningLock<T>` for the given `BorrowingReference<T>` to guarantee safe
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.
123
+ */
124
+ [[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
+ inline bool hasValue() const {
133
+ return _value != nullptr && !_state->isDeleted;
81
134
  }
82
135
 
83
136
  /**
84
- Try to lock the borrowing reference to an owning reference, or `nullptr` if it has already been deleted.
137
+ Get a borrowing (or "weak") reference to this owning reference
85
138
  */
86
139
  [[nodiscard]]
87
- OwningReference<T> lock() const;
140
+ WeakReference<T> weak() const {
141
+ return WeakReference(*this);
142
+ }
143
+
144
+ /**
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.
149
+ */
150
+ void destroy() {
151
+ std::unique_lock lock(_state->mutex);
152
+
153
+ forceDestroyValue();
154
+ }
88
155
 
89
156
  public:
90
- friend class OwningReference<T>;
157
+ explicit inline operator bool() const {
158
+ return hasValue();
159
+ }
91
160
 
92
- private:
93
- void maybeDestroy() {
94
- _mutex->lock();
161
+ inline T& operator*() const {
162
+ #ifdef NITRO_DEBUG
163
+ if (!hasValue()) [[unlikely]] {
164
+ throw std::runtime_error("Tried to dereference (*) nullptr BorrowingReference<T>!");
165
+ }
166
+ #endif
167
+ return *_value;
168
+ }
169
+
170
+ inline T* operator->() const {
171
+ #ifdef NITRO_DEBUG
172
+ if (!hasValue()) [[unlikely]] {
173
+ throw std::runtime_error("Tried to dereference (->) nullptr BorrowingReference<T>!");
174
+ }
175
+ #endif
176
+ return _value;
177
+ }
178
+
179
+ inline bool operator==(T* other) const {
180
+ return _value == other;
181
+ }
182
+
183
+ inline bool operator!=(T* other) const {
184
+ return _value != other;
185
+ }
186
+
187
+ inline bool operator==(const BorrowingReference<T>& other) const {
188
+ return _value == other._value;
189
+ }
190
+
191
+ inline bool operator!=(const BorrowingReference<T>& other) const {
192
+ return _value != other._value;
193
+ }
95
194
 
96
- if (*_strongRefCount == 0 && *_weakRefCount == 0) {
195
+ private:
196
+ void maybeDestroyState() {
197
+ if (_state->strongRefCount == 0 && _state->weakRefCount == 0) {
97
198
  // free the full memory if there are no more references at all
98
- if (!(*_isDeleted)) {
99
- delete _value;
100
- }
101
- delete _isDeleted;
102
- delete _strongRefCount;
103
- delete _weakRefCount;
104
- _mutex->unlock();
199
+ delete _state;
200
+ _state = nullptr;
105
201
  return;
106
202
  }
203
+ }
107
204
 
108
- _mutex->unlock();
205
+ void forceDestroyValue() {
206
+ if (_state->isDeleted) [[unlikely]] {
207
+ // it has already been destroyed.
208
+ return;
209
+ }
210
+ delete _value;
211
+ _value = nullptr;
212
+ _state->isDeleted = true;
109
213
  }
110
214
 
215
+ public:
216
+ friend class WeakReference<T>;
217
+ friend class OwningLock<T>;
218
+
111
219
  private:
112
220
  T* _value;
113
- bool* _isDeleted;
114
- std::atomic_size_t* _strongRefCount;
115
- std::atomic_size_t* _weakRefCount;
116
- std::recursive_mutex* _mutex;
221
+ ReferenceState* _state;
117
222
  };
118
223
 
119
224
  } // namespace margelo::nitro
225
+
226
+ #include "WeakReference+Owning.hpp"
@@ -9,7 +9,7 @@
9
9
  #define NitroDefines_h
10
10
 
11
11
  // Sets the version of the native Nitro core library
12
- #define NITRO_VERSION "0.22.0"
12
+ #define NITRO_VERSION "0.23.0"
13
13
 
14
14
  // Sets whether to use debug or optimized production build flags
15
15
  #ifdef DEBUG
@@ -52,14 +52,4 @@
52
52
  #define SWIFT_NONCOPYABLE
53
53
  #endif
54
54
 
55
- // React Native Support
56
- #if __has_include(<cxxreact/ReactNativeVersion.h>)
57
- #include <cxxreact/ReactNativeVersion.h>
58
- #endif
59
- #ifndef REACT_NATIVE_VERSION_MINOR
60
- #define REACT_NATIVE_VERSION_MAJOR 0
61
- #define REACT_NATIVE_VERSION_MINOR 0
62
- #define REACT_NATIVE_VERSION_PATCH 0
63
- #endif
64
-
65
55
  #endif /* NitroDefines_h */
@@ -9,10 +9,10 @@
9
9
 
10
10
  namespace margelo::nitro {
11
11
  template <typename T>
12
- class OwningReference;
12
+ class BorrowingReference;
13
13
  }
14
14
 
15
- #include "OwningReference.hpp"
15
+ #include "BorrowingReference.hpp"
16
16
  #include <cstddef>
17
17
  #include <mutex>
18
18
 
@@ -20,21 +20,26 @@ namespace margelo::nitro {
20
20
 
21
21
  /**
22
22
  * An `OwningLock<T>` is a RAII instance that locks the given caller thread guaranteed safe access
23
- * to a `OwningReference<T>`.
24
- * The `OwningReference<T>` cannot be deleted while an `OwningLock<T>` of it is alive.
23
+ * to a `BorrowingReference<T>`.
24
+ * The `BorrowingReference<T>` cannot be deleted while an `OwningLock<T>` of it is alive.
25
25
  *
26
26
  * This is useful in JSI, because Hermes runs garbage collection on a separate Thread,
27
- * and the separate Thread can delete an `OwningReference<T>` while it's still in use.
27
+ * and the separate Thread can delete an `BorrowingReference<T>` while it's still in use.
28
28
  * The `OwningLock<T>` prevents exactly this problem by blocking the GC destructor until
29
29
  * the `OwningLock<T>` is released.
30
30
  *
31
- * To create an `OwningLock<T>`, simply call `lock()` on an `OwningReference<T>`.
31
+ * To create an `OwningLock<T>`, simply call `lock()` on an `BorrowingReference<T>`.
32
32
  */
33
33
  template <typename T>
34
34
  class OwningLock final {
35
+ private:
36
+ explicit OwningLock(const BorrowingReference<T>& reference) : _reference(reference) {
37
+ _reference._state->mutex.lock();
38
+ }
39
+
35
40
  public:
36
41
  ~OwningLock() {
37
- _reference._mutex->unlock();
42
+ _reference._state->mutex.unlock();
38
43
  }
39
44
 
40
45
  OwningLock() = delete;
@@ -42,15 +47,10 @@ public:
42
47
  OwningLock(OwningLock&&) = delete;
43
48
 
44
49
  private:
45
- explicit OwningLock(const OwningReference<T>& reference) : _reference(reference) {
46
- _reference._mutex->lock();
47
- }
48
-
49
- private:
50
- OwningReference<T> _reference;
50
+ BorrowingReference<T> _reference;
51
51
 
52
52
  private:
53
- friend class OwningReference<T>;
53
+ friend class BorrowingReference<T>;
54
54
  };
55
55
 
56
56
  } // namespace margelo::nitro