react-native-nitro-modules 0.1.7 → 0.2.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 (37) hide show
  1. package/README.md +7 -3
  2. package/android/CMakeLists.txt +1 -0
  3. package/cpp/core/HybridFunction.hpp +167 -0
  4. package/cpp/core/HybridObject.cpp +37 -128
  5. package/cpp/core/HybridObject.hpp +13 -149
  6. package/cpp/jsi/JSICache.cpp +24 -8
  7. package/cpp/jsi/JSICache.hpp +31 -23
  8. package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +7 -3
  9. package/cpp/jsi/JSIConverter+Function.hpp +1 -1
  10. package/cpp/jsi/JSIConverter+HybridObject.hpp +26 -87
  11. package/cpp/jsi/Promise.cpp +2 -2
  12. package/cpp/prototype/HybridObjectPrototype.cpp +87 -0
  13. package/cpp/prototype/HybridObjectPrototype.hpp +92 -0
  14. package/cpp/prototype/Prototype.hpp +163 -0
  15. package/cpp/prototype/PrototypeChain.hpp +78 -0
  16. package/cpp/turbomodule/NativeNitroModules.cpp +3 -3
  17. package/cpp/turbomodule/NativeNitroModules.hpp +1 -1
  18. package/cpp/utils/BorrowingReference+Owning.hpp +1 -1
  19. package/cpp/utils/BorrowingReference.hpp +1 -1
  20. package/lib/HybridObject.d.ts +20 -1
  21. package/lib/tsconfig.tsbuildinfo +1 -1
  22. package/lib/typescript/HybridObject.d.ts +20 -1
  23. package/lib/typescript/HybridObject.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/src/HybridObject.ts +20 -1
  26. package/cpp/core/PointerHolder.hpp +0 -94
  27. package/lib/typescript/AnyMap.d.ts +0 -17
  28. package/lib/typescript/AnyMap.d.ts.map +0 -1
  29. package/lib/typescript/ModuleNotFoundError.d.ts +0 -7
  30. package/lib/typescript/ModuleNotFoundError.d.ts.map +0 -1
  31. package/lib/typescript/NativeNitroModules.d.ts +0 -13
  32. package/lib/typescript/NativeNitroModules.d.ts.map +0 -1
  33. package/lib/typescript/NativeNitroModules.web.d.ts +0 -5
  34. package/lib/typescript/NativeNitroModules.web.d.ts.map +0 -1
  35. package/lib/typescript/NitroModules.d.ts +0 -18
  36. package/lib/typescript/__tests__/index.test.d.ts +0 -1
  37. package/lib/typescript/__tests__/index.test.d.ts.map +0 -1
@@ -0,0 +1,163 @@
1
+ //
2
+ // PrototypeChain.hpp
3
+ // NitroModules
4
+ //
5
+ // Created by Marc Rousavy on 07.08.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "HybridFunction.hpp"
11
+ #include <memory>
12
+ #include <string>
13
+ #include <typeindex>
14
+ #include <unordered_map>
15
+
16
+ namespace margelo::nitro {
17
+
18
+ /**
19
+ * Represents a Prototype's native C++ type ID.
20
+ * This can be used to identify a prototype against a C++ instance,
21
+ * or used as a cache-key.
22
+ */
23
+ using NativeInstanceId = std::type_index;
24
+
25
+ /**
26
+ * Represents a JS `Prototype`'s structure.
27
+ * Every prototype has a related C++ type ID (`instanceTypeId`).
28
+ * Prototypes can be sub-classes, in which case they have a `base` prototype.
29
+ * Each prototype has a list of methods, and properties (getters + setters).
30
+ *
31
+ * By using this `Prototype` structure, we can create JS objects that act
32
+ * as prototypes for `HybridObject`s.
33
+ *
34
+ * While a `Prototype` actually holds all the methods, a `HybridObject` only
35
+ * contains state and memory.
36
+ * This way the JS VM doesn't need to re-create methods for each `HybridObject`,
37
+ * they are only initialized once on the shared `Prototype`.
38
+ */
39
+ struct Prototype final {
40
+ private:
41
+ std::shared_ptr<Prototype> _base = nullptr;
42
+ NativeInstanceId _instanceTypeId;
43
+ std::unordered_map<std::string, HybridFunction> _methods;
44
+ std::unordered_map<std::string, HybridFunction> _getters;
45
+ std::unordered_map<std::string, HybridFunction> _setters;
46
+
47
+ private:
48
+ Prototype(const NativeInstanceId& typeId, const std::shared_ptr<Prototype>& base) : _instanceTypeId(typeId), _base(base) {}
49
+
50
+ public:
51
+ /**
52
+ * Gets a `Prototype` specification/node for the given native C++ type ID.
53
+ *
54
+ * If the given C++ type ID is unknown, a new `Prototype` node is created,
55
+ * which has to be initialized with methods, getters and setters first.
56
+ *
57
+ * If the given C++ type ID is already known in the static `Prototype` tree,
58
+ * a shared reference to it is returned.
59
+ */
60
+ static std::shared_ptr<Prototype> get(const NativeInstanceId& typeId, const std::shared_ptr<Prototype>& base = nullptr) {
61
+ static std::unordered_map<NativeInstanceId, std::shared_ptr<Prototype>> _prototypesCache;
62
+
63
+ const auto& found = _prototypesCache.find(typeId);
64
+ if (found != _prototypesCache.end()) {
65
+ // We know this C++ type ID / Prototype - return it!
66
+ return found->second;
67
+ } else {
68
+ // This is the first time we see this C++ type ID - create a new base Prototype for this.
69
+ auto prototype = std::shared_ptr<Prototype>(new Prototype(typeId, base));
70
+ _prototypesCache.emplace(typeId, prototype);
71
+ return prototype;
72
+ }
73
+ }
74
+
75
+ public:
76
+ template <typename T>
77
+ inline bool isNativeInstance() const noexcept {
78
+ return _instanceTypeId == std::type_index(typeid(T));
79
+ }
80
+
81
+ inline bool hasHybrids() const {
82
+ return !_methods.empty() || !_getters.empty() || !_setters.empty();
83
+ }
84
+
85
+ inline bool hasBase() const noexcept {
86
+ return _base != nullptr;
87
+ }
88
+ inline const std::shared_ptr<Prototype>& getBase() const noexcept {
89
+ return _base;
90
+ }
91
+ inline const NativeInstanceId& getNativeInstanceId() const noexcept {
92
+ return _instanceTypeId;
93
+ }
94
+ inline const std::unordered_map<std::string, HybridFunction>& getMethods() const noexcept {
95
+ return _methods;
96
+ }
97
+ inline const std::unordered_map<std::string, HybridFunction>& getGetters() const noexcept {
98
+ return _getters;
99
+ }
100
+ inline const std::unordered_map<std::string, HybridFunction>& getSetters() const noexcept {
101
+ return _setters;
102
+ }
103
+
104
+ public:
105
+ /**
106
+ * Registers the given C++ method as a Hybrid Method that can be called from JS, through the object's Prototype.
107
+ * Example:
108
+ * ```cpp
109
+ * registerHybridMethod("sayHello", &MyObject::sayHello);
110
+ * ```
111
+ */
112
+ template <typename Derived, typename ReturnType, typename... Args>
113
+ inline void registerHybridMethod(std::string name, ReturnType (Derived::*method)(Args...)) {
114
+ if (_getters.contains(name) || _setters.contains(name)) [[unlikely]] {
115
+ throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a property with that name already exists!");
116
+ }
117
+ if (_methods.contains(name)) [[unlikely]] {
118
+ throw std::runtime_error("Cannot add Hybrid Method \"" + name + "\" - a method with that name already exists!");
119
+ }
120
+
121
+ _methods.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionType::METHOD));
122
+ }
123
+
124
+ /**
125
+ * Registers the given C++ method as a property getter that can be called from JS, through the object's Prototype.
126
+ * Example:
127
+ * ```cpp
128
+ * registerHybridGetter("foo", &MyObject::getFoo);
129
+ * ```
130
+ */
131
+ template <typename Derived, typename ReturnType>
132
+ inline void registerHybridGetter(std::string name, ReturnType (Derived::*method)()) {
133
+ if (_getters.contains(name)) [[unlikely]] {
134
+ throw std::runtime_error("Cannot add Hybrid Property Getter \"" + name + "\" - a getter with that name already exists!");
135
+ }
136
+ if (_methods.contains(name)) [[unlikely]] {
137
+ throw std::runtime_error("Cannot add Hybrid Property Getter \"" + name + "\" - a method with that name already exists!");
138
+ }
139
+
140
+ _getters.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionType::GETTER));
141
+ }
142
+
143
+ /**
144
+ * Registers the given C++ method as a property setter that can be called from JS, through the object's Prototype.
145
+ * Example:
146
+ * ```cpp
147
+ * registerHybridSetter("foo", &MyObject::setFoo);
148
+ * ```
149
+ */
150
+ template <typename Derived, typename ValueType>
151
+ inline void registerHybridSetter(std::string name, void (Derived::*method)(ValueType)) {
152
+ if (_setters.contains(name)) [[unlikely]] {
153
+ throw std::runtime_error("Cannot add Hybrid Property Setter \"" + name + "\" - a setter with that name already exists!");
154
+ }
155
+ if (_methods.contains(name)) [[unlikely]] {
156
+ throw std::runtime_error("Cannot add Hybrid Property Setter \"" + name + "\" - a method with that name already exists!");
157
+ }
158
+
159
+ _setters.emplace(name, HybridFunction::createHybridFunction(name, method, FunctionType::SETTER));
160
+ }
161
+ };
162
+
163
+ } // namespace margelo::nitro
@@ -0,0 +1,78 @@
1
+ //
2
+ // PrototypeChain.hpp
3
+ // NitroModules
4
+ //
5
+ // Created by Marc Rousavy on 07.08.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "Prototype.hpp"
11
+
12
+ namespace margelo::nitro {
13
+
14
+ /**
15
+ * Represents a mutable chain of prototypes.
16
+ * Callers can use this class to incrementally add sub-classes to prototypes and build
17
+ * prototype chains/trees using C++ type information.
18
+ *
19
+ * The template methods can be used to find a specific C++ instance in the prototype tree,
20
+ * or create a new sub-class if it cannot be found.
21
+ */
22
+ class PrototypeChain final {
23
+ private:
24
+ std::shared_ptr<Prototype> _prototype;
25
+
26
+ public:
27
+ PrototypeChain() {}
28
+
29
+ public:
30
+ /**
31
+ * Gets the current `Prototype` as a whole.
32
+ * This does not do any modifications to the Prototype tree.
33
+ */
34
+ inline const std::shared_ptr<Prototype>& getPrototype() const {
35
+ return _prototype;
36
+ }
37
+
38
+ public:
39
+ /**
40
+ * Extends the Prototype with the given type `Derived`.
41
+ * If the Prototype already extended `Derived`, this returns the current state.
42
+ */
43
+ template <typename Derived>
44
+ inline const std::shared_ptr<Prototype>& extendPrototype() {
45
+ if (_prototype == nullptr) {
46
+ _prototype = Prototype::get(typeid(Derived));
47
+ }
48
+
49
+ return getOrExtendPrototype<Derived>(_prototype);
50
+ }
51
+
52
+ private:
53
+ /**
54
+ * Perform a bottom-down search of the given `Derived` C++ type info.
55
+ * If the current prototype tree does not have a Prototype that represents the
56
+ * C++ type `Derived`, it will extend it at the bottom and shift the `Prototype` tree
57
+ * up by one.
58
+ */
59
+ template <typename Derived>
60
+ inline const std::shared_ptr<Prototype>& getOrExtendPrototype(const std::shared_ptr<Prototype>& node) {
61
+ if (node->isNativeInstance<Derived>()) {
62
+ // If the Prototype represents the caller type (`Derived`), we work with this Prototype.
63
+ return node;
64
+ } else {
65
+ if (node->hasBase()) {
66
+ // We didn't find a match in this prototype, let's recursively try it's parent!
67
+ return getOrExtendPrototype<Derived>(node->getBase());
68
+ } else {
69
+ // We didn't find `Derived` and we don't have a base- add a child and shift the tree by one.
70
+ auto newBase = _prototype;
71
+ _prototype = Prototype::get(typeid(Derived), newBase);
72
+ return _prototype;
73
+ }
74
+ }
75
+ }
76
+ };
77
+
78
+ } // namespace margelo::nitro
@@ -58,12 +58,12 @@ void NativeNitroModules::install(jsi::Runtime& runtime) {
58
58
  Dispatcher::installRuntimeGlobalDispatcher(runtime, dispatcher);
59
59
  }
60
60
 
61
- jsi::Object NativeNitroModules::createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName,
62
- const std::optional<jsi::Object>& args) {
61
+ jsi::Value NativeNitroModules::createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName,
62
+ const std::optional<jsi::Object>& args) {
63
63
  auto name = hybridObjectName.utf8(runtime);
64
64
  // TODO: Pass args? Do we need that?
65
65
  auto hybridObject = HybridObjectRegistry::createHybridObject(name.c_str());
66
- return jsi::Object::createFromHostObject(runtime, hybridObject);
66
+ return hybridObject->toObject(runtime);
67
67
  }
68
68
 
69
69
  } // namespace facebook::react
@@ -23,7 +23,7 @@ public:
23
23
  jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& propName) override;
24
24
 
25
25
  void install(jsi::Runtime& runtime);
26
- jsi::Object createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName, const std::optional<jsi::Object>& args);
26
+ jsi::Value createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName, const std::optional<jsi::Object>& args);
27
27
 
28
28
  public:
29
29
  constexpr static auto kModuleName = "NitroModulesCxx";
@@ -22,7 +22,7 @@ BorrowingReference<T>::BorrowingReference(const OwningReference<T>& ref) {
22
22
  }
23
23
 
24
24
  template <typename T>
25
- OwningReference<T> BorrowingReference<T>::lock() {
25
+ OwningReference<T> BorrowingReference<T>::lock() const {
26
26
  std::unique_lock lock(*_mutex);
27
27
 
28
28
  if (*_isDeleted) {
@@ -82,7 +82,7 @@ public:
82
82
  Try to lock the borrowing reference to an owning reference, or `nullptr` if it has already been deleted.
83
83
  */
84
84
  [[nodiscard]]
85
- OwningReference<T> lock();
85
+ OwningReference<T> lock() const;
86
86
 
87
87
  public:
88
88
  friend class OwningReference<T>;
@@ -9,7 +9,10 @@ export interface PlatformSpec {
9
9
  * Represents a Nitro `HybridObject` which is implemented natively in either C++,
10
10
  * or Swift/Kotlin.
11
11
  *
12
- * Uses the Nitro Tunnel for efficient, low-overhead JS <-> Native communication.
12
+ * `HybridObject`s use the Nitro Tunnel for efficient, low-overhead JS <-> Native communication.
13
+ *
14
+ * All `HybridObject`s are implemented using `NativeState`, and inherit their properties
15
+ * and methods from their prototype, so the actual JS object is empty.
13
16
  *
14
17
  * @example
15
18
  * ```ts
@@ -22,6 +25,22 @@ export interface PlatformSpec {
22
25
  * ```
23
26
  */
24
27
  export interface HybridObject<Spec extends PlatformSpec> {
28
+ /**
29
+ * Holds a type-name describing the native `HybridObject` instance.
30
+ *
31
+ * This is the only property actually present on the actual JavaScript object,
32
+ * because all other properties and methods are inherited from a shared Prototype.
33
+ *
34
+ * Nitro prototypes also have a `__type`.
35
+ *
36
+ * For actual HybridObject instances, this is `NativeState<...>`, for
37
+ * prototypes this is `Prototype<...>`.
38
+ *
39
+ * @internal
40
+ * @private
41
+ * @note This value is available in debug only.
42
+ */
43
+ readonly __type?: string;
25
44
  /**
26
45
  * The `HybridObject`'s name.
27
46
  */