react-native-nitro-modules 0.0.6 → 0.0.8

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/NitroModules.podspec +1 -1
  2. package/README.md +210 -0
  3. package/android/CMakeLists.txt +1 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/cpp/JNIOnLoad.cpp +17 -0
  6. package/android/src/main/cpp/core/JHybridObject.cpp +8 -0
  7. package/android/src/main/cpp/core/JHybridObject.hpp +25 -0
  8. package/android/src/main/cpp/platform/ThreadUtils.cpp +35 -0
  9. package/android/src/main/cpp/registry/JHybridObjectInitializer.hpp +29 -0
  10. package/android/src/main/cpp/registry/JHybridObjectRegistry.cpp +35 -0
  11. package/android/src/main/cpp/registry/JHybridObjectRegistry.hpp +30 -0
  12. package/android/src/main/cpp/utils/JNISharedPtr.h +36 -0
  13. package/android/src/main/java/com/margelo/nitro/HybridObject.kt +61 -0
  14. package/android/src/main/java/com/margelo/nitro/HybridObjectInitializer.java +13 -0
  15. package/android/src/main/java/com/margelo/nitro/HybridObjectRegistry.java +30 -0
  16. package/android/src/main/java/com/margelo/nitro/NitroModulesPackage.java +26 -0
  17. package/cpp/core/HybridObject.hpp +23 -8
  18. package/cpp/core/PointerHolder.hpp +2 -1
  19. package/cpp/jsi/JSICache.hpp +6 -3
  20. package/cpp/jsi/JSIConverter.hpp +50 -39
  21. package/cpp/templates/CountTrailingOptionals.hpp +64 -0
  22. package/cpp/templates/FutureType.hpp +28 -0
  23. package/cpp/templates/IsHostObject.hpp +27 -0
  24. package/cpp/templates/IsInPack.hpp +21 -0
  25. package/cpp/templates/IsNativeState.hpp +27 -0
  26. package/cpp/threading/Dispatcher.hpp +2 -1
  27. package/cpp/utils/BorrowingReference+Owning.hpp +4 -2
  28. package/cpp/utils/BorrowingReference.hpp +5 -3
  29. package/cpp/utils/NitroHash.hpp +2 -1
  30. package/cpp/utils/NitroLogger.hpp +6 -3
  31. package/cpp/utils/OwningLock.hpp +4 -2
  32. package/cpp/utils/OwningReference.hpp +5 -4
  33. package/cpp/utils/TypeInfo.hpp +4 -2
  34. package/lib/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +8 -36
  36. package/cpp/utils/DoesClassExist.hpp +0 -23
  37. /package/cpp/{jsi → core}/ArrayBuffer.hpp +0 -0
@@ -28,11 +28,11 @@ Pod::Spec.new do |s|
28
28
  ]
29
29
  s.public_header_files = [
30
30
  # Public C++ headers will be exposed in modulemap (for Swift)
31
+ "cpp/core/ArrayBuffer.hpp",
31
32
  "cpp/core/HybridObject.hpp",
32
33
  "cpp/core/HybridContext.hpp",
33
34
  "cpp/registry/HybridObjectRegistry.hpp",
34
35
  "cpp/jsi/JSIConverter.hpp",
35
- "cpp/jsi/ArrayBuffer.hpp",
36
36
  "cpp/threading/Dispatcher.hpp",
37
37
  "cpp/utils/NitroHash.hpp",
38
38
  "cpp/utils/NitroDefines.hpp",
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ <a href="https://margelo.io">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="../../docs/img/banner-react-native-nitro-modules-dark.png" />
4
+ <source media="(prefers-color-scheme: light)" srcset="../../docs/img/banner-react-native-nitro-modules-light.png" />
5
+ <img alt="Nitrogen" src="../../docs/img/banner-react-native-nitro-modules-light.png" />
6
+ </picture>
7
+ </a>
8
+
9
+ <br />
10
+
11
+ **react-native-nitro-modules** is a core library that contains highly efficient statically compiled JS to C++ bindings.
12
+
13
+ It uses JSI to generate C++ templates that can bridge virtually any JS type to a C++ type with minimal overhead.
14
+
15
+ ## Installation
16
+
17
+ ### Inside an app
18
+
19
+ Install [react-native-nitro-modules](https://npmjs.org/react-native-nitro-modules) as a `dependency` in your react-native app:
20
+ ```sh
21
+ npm i react-native-nitro-modules
22
+ cd ios && pod install
23
+ ```
24
+
25
+ ### Inside a nitro module library
26
+
27
+ If you are building a nitro module yourself, add [react-native-nitro-modules](https://npmjs.org/react-native-nitro-modules) as a `peerDependency` into your library's `package.json`:
28
+
29
+ ```json
30
+ {
31
+ ...
32
+ "peerDependencies": {
33
+ ...
34
+ "react-native-nitro-modules": "*"
35
+ },
36
+ }
37
+ ```
38
+
39
+ Then install `react-native-nitro-modules` as a normal `dependency` in your library's `example/` app as seen above.
40
+
41
+ ## Usage
42
+
43
+ **react-native-nitro-modules** can either be used with-, or without nitrogen, or mixed (some objects are automatically generated, some manually).
44
+
45
+ ### With Nitrogen
46
+
47
+ When using Nitrogen, all the bindings are automatically generated. You only need to implement C++, Swift or Kotlin interfaces inside your codebase.
48
+
49
+ ### Without Nitrogen
50
+
51
+ All C++ bindings are bridged to JS using "Hybrid Objects".
52
+
53
+ A Hybrid Object can have both methods and properties (get and set).
54
+ Create a C++ Hybrid Object by inheriting from `HybridObject`:
55
+
56
+ ```cpp
57
+ #include <NitroModules/HybridObject.hpp>
58
+
59
+ using namespace margelo::nitro;
60
+
61
+ class MyHybridObject: public HybridObject {
62
+ public:
63
+ explicit MyHybridObject(): HybridObject(TAG) {}
64
+
65
+ public:
66
+ // Property (get)
67
+ double getNumber() { return 13; }
68
+ // Property (set)
69
+ void setNumber(double value) { }
70
+ // Method
71
+ double add(double left, double right) { return left + right; }
72
+
73
+ public:
74
+ void loadHybridMethods() override {
75
+ // Register all methods that need to be exposed to JS
76
+ registerHybridGetter("number", &MyHybridObject::getNumber, this);
77
+ registerHybridSetter("number", &MyHybridObject::setNumber, this);
78
+ registerHybridMethod("add", &MyHybridObject::add, this);
79
+ }
80
+
81
+ private:
82
+ static constexpr auto TAG = "MyHybrid";
83
+ };
84
+ ```
85
+
86
+ The `MyHybridObject` can then be registered in the [`HybridObjectRegistry`](./cpp/registry/HybridObjectRegistry.hpp) at app startup:
87
+
88
+ ```cpp
89
+ #include <NitroModules/HybridObjectRegistry.hpp>
90
+
91
+ // Call this at app startup to register the HybridObjects
92
+ void load() {
93
+ HybridObjectRegistry::registerHybridObjectConstructor(
94
+ "MyHybrid",
95
+ []() -> std::shared_ptr<HybridObject> {
96
+ return std::make_shared<MyHybridObject>();
97
+ }
98
+ );
99
+ }
100
+ ```
101
+
102
+ Inside your `MyHybridObject`, you can use standard C++ types which will automatically be converted to JS using Nitro's [`JSIConverter<T>`](./cpp/jsi/JSIConverter.hpp) interface.
103
+
104
+ The following C++ / JS types are supported out of the box:
105
+
106
+ <table>
107
+ <tr>
108
+ <th>JS Type</th>
109
+ <th>C++ Type</th>
110
+ </tr>
111
+
112
+ <tr>
113
+ <td><code>number</code></td>
114
+ <td><code>double</code> / <code>int</code> / <code>float</code></td>
115
+ </tr>
116
+ <tr>
117
+ <td><code>boolean</code></td>
118
+ <td><code>bool</code></td>
119
+ </tr>
120
+ <tr>
121
+ <td><code>string</code></td>
122
+ <td><code>std::string</code></td>
123
+ </tr>
124
+ <tr>
125
+ <td><code>bigint</code></td>
126
+ <td><code>int64_t</code> / <code>uint64_t</code></td>
127
+ </tr>
128
+ <tr>
129
+ <td><code>T[]</code></td>
130
+ <td><code>std::vector&lt;T&gt;</code></td>
131
+ </tr>
132
+ <tr>
133
+ <td><code>[A, B, C, ...]</code></td>
134
+ <td><code>std::tuple&lt;A, B, C, ...&gt;</code></td>
135
+ </tr>
136
+ <tr>
137
+ <td><code>A | B | C | ...</code></td>
138
+ <td><code>std::variant&lt;A, B, C, ...&gt;</code></td>
139
+ </tr>
140
+ <tr>
141
+ <td><code>Record&lt;string, T&gt;</code></td>
142
+ <td><code>std::unordered_map&lt;std::string, T&gt;</code></td>
143
+ </tr>
144
+ <tr>
145
+ <td><code>T?</code></td>
146
+ <td><code>std::optional&lt;T&gt;</code></td>
147
+ </tr>
148
+ <tr>
149
+ <td><code>Promise&lt;T&gt;</code></td>
150
+ <td><code>std::future&lt;T&gt;</code></td>
151
+ </tr>
152
+ <tr>
153
+ <td><code>(TArgs...) =&gt; TReturn</code></td>
154
+ <td><code>std::function&lt;std::future&lt;TReturn&gt; (TArgs...)&gt;</code></td>
155
+ </tr>
156
+ <tr>
157
+ <td><code>{ ... }</code></td>
158
+ <td><code>std::shared_ptr&lt;<a href="./cpp/core/AnyMap.hpp">AnyMap</a>&gt;</code></td>
159
+ </tr>
160
+ <tr>
161
+ <td><code>ArrayBuffer</code></td>
162
+ <td><code>std::shared_ptr&lt;<a href="./cpp/core/ArrayBuffer.hpp">ArrayBuffer</a>&gt;</code></td>
163
+ </tr>
164
+ <tr>
165
+ <td><code><a href="./src/HybridObject.ts">HybridObject</a></code></td>
166
+ <td><code>std::shared_ptr&lt;<a href="./cpp/core/HybridObject.hpp">HybridObject</a>&gt;</code></td>
167
+ </tr>
168
+ </table>
169
+
170
+
171
+ Since the `JSIConverter<T>` is just a template, you can extend it with any other custom types by overloading the interface.
172
+
173
+ For example, to add support for an enum, overload `JSIConverter<YourEnum>`:
174
+
175
+ ```cpp
176
+ #include <NitroModules/JSIConverter.hpp>
177
+
178
+ enum class MyEnum {
179
+ FIRST = 0,
180
+ SECOND = 1
181
+ };
182
+
183
+ namespace margelo::nitro {
184
+ template <>
185
+ struct JSIConverter<MyEnum> {
186
+ static inline MyEnum fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
187
+ int intValue = JSIConverter<int>::fromJSI(runtime, arg);
188
+ return static_cast<MyEnum>(intValue);
189
+ }
190
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, MyEnum arg) {
191
+ int intValue = static_cast<int>(arg);
192
+ return JSIConverter<int>::toJSI(runtime, intValue);
193
+ }
194
+ };
195
+ }
196
+ ```
197
+
198
+ ..and on the JS side, you can implicitly cast the `number` to an enum as well:
199
+
200
+ ```js
201
+ enum MyEnum {
202
+ FIRST = 0,
203
+ SECOND = 1
204
+ }
205
+ const value = myHybridObject.getEnumValue() // <-- typed as `MyEnum` instead of `number`
206
+ ```
207
+
208
+ Make sure to always include the header that defines the `JSIConverter<MyEnum>` overload inside the `MyHybridObject` file, as this is where the `JSIConverter<T>` overloads are accessed from.
209
+
210
+ [**Nitrogen**](../nitrogen/) can automatically generate such `JSIConverter<T>` extensions for enums, TypeScript unions, and even structs/objects - so it is generally recommended to use nitrogen.
@@ -26,6 +26,7 @@ include_directories(
26
26
  ../cpp/platform
27
27
  ../cpp/registry
28
28
  ../cpp/test-object
29
+ ../cpp/templates
29
30
  ../cpp/threading
30
31
  ../cpp/turbomodule
31
32
  ../cpp/utils
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,17 @@
1
+ /// Entry point for JNI.
2
+
3
+ #include "JHybridObjectRegistry.hpp"
4
+ #include "RegisterNativeNitroModules.hpp"
5
+ #include <jni.h>
6
+
7
+ using namespace margelo::nitro;
8
+
9
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
10
+ // 1. Initialize the Nitro JSI Turbo Module
11
+ RegisterNativeNitroModules::registerNativeNitroModules();
12
+
13
+ // 2. Initialize all Java bindings
14
+ JHybridObjectRegistry::registerNatives();
15
+
16
+ return JNI_VERSION_1_2;
17
+ }
@@ -0,0 +1,8 @@
1
+ //
2
+ // JHybridObject.hpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ namespace margelo::nitro {} // namespace margelo::nitro
@@ -0,0 +1,25 @@
1
+ //
2
+ // JHybridObject.hpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "HybridObject.hpp"
11
+ #include <fbjni/fbjni.h>
12
+
13
+ namespace margelo::nitro {
14
+
15
+ using namespace facebook;
16
+
17
+ struct JHybridObject : public jni::HybridClass<JHybridObject>, public HybridObject {
18
+ public:
19
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/HybridObject;";
20
+
21
+ public:
22
+ static void registerNatives();
23
+ };
24
+
25
+ } // namespace margelo::nitro
@@ -0,0 +1,35 @@
1
+ //
2
+ // ThreadUtils.cpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ #include "ThreadUtils.hpp"
9
+ #include <pthread.h>
10
+ #include <sstream>
11
+ #include <string>
12
+ #include <thread>
13
+
14
+ namespace margelo::nitro {
15
+
16
+ std::string ThreadUtils::getThreadName() {
17
+ #ifdef HAVE_ANDROID_PTHREAD_SETNAME_NP
18
+ // Try using pthread APIs
19
+ pthread_t this_thread = pthread_self();
20
+ char thread_name[16]; // Thread name length limit in Android is 16 characters
21
+
22
+ int result = pthread_getname_np(this_thread, thread_name, sizeof(thread_name));
23
+ if (result == 0) {
24
+ return std::string(thread_name);
25
+ }
26
+ #endif
27
+
28
+ // Fall back to this_thread ID
29
+ std::stringstream stream;
30
+ stream << std::this_thread::get_id();
31
+ std::string threadId = stream.str();
32
+ return "Thread #" + threadId;
33
+ }
34
+
35
+ } // namespace margelo::nitro
@@ -0,0 +1,29 @@
1
+ //
2
+ // JHybridObjectInitializer.hpp
3
+ // DoubleConversion
4
+ //
5
+ // Created by Marc Rousavy on 22.07.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "HybridObject.hpp"
11
+ #include "JHybridObject.hpp"
12
+ #include <fbjni/fbjni.h>
13
+
14
+ namespace margelo::nitro {
15
+
16
+ using namespace facebook;
17
+
18
+ struct JHybridObjectInitializer : public jni::JavaClass<JHybridObjectInitializer> {
19
+ public:
20
+ jni::local_ref<JHybridObject::javaobject> call() const {
21
+ const auto method = this->getClass()->getMethod<JHybridObject::javaobject()>("initialize");
22
+ return method(self());
23
+ }
24
+
25
+ public:
26
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/HybridObjectInitializer;";
27
+ };
28
+
29
+ } // namespace margelo::nitro
@@ -0,0 +1,35 @@
1
+ //
2
+ // JHybridObjectRegistry.cpp
3
+ // DoubleConversion
4
+ //
5
+ // Created by Marc Rousavy on 22.07.24.
6
+ //
7
+
8
+ #include "JHybridObjectRegistry.hpp"
9
+ #include "HybridObjectRegistry.hpp"
10
+ #include "JNISharedPtr.h"
11
+
12
+ namespace margelo::nitro {
13
+
14
+ void JHybridObjectRegistry::registerHybridObjectConstructor(jni::alias_ref<jni::JClass> clazz, std::string hybridObjectName,
15
+ jni::alias_ref<JHybridObjectInitializer> constructorFn) {
16
+ auto sharedInitializer = jni::make_global(constructorFn);
17
+ HybridObjectRegistry::registerHybridObjectConstructor(hybridObjectName, [=]() -> std::shared_ptr<HybridObject> {
18
+ // 1. Call the Java initializer function
19
+ jni::local_ref<JHybridObject::javaobject> hybridObject = sharedInitializer->call();
20
+ // 2. Make the resulting HybridObject a global (shared) reference
21
+ jni::global_ref<JHybridObject::javaobject> globalHybridObject = jni::make_global(hybridObject);
22
+ // 3. Create a shared_ptr from the JNI global reference
23
+ std::shared_ptr<JHybridObject> sharedCppPart = JNISharedPtr::make_shared_from_jni<JHybridObject>(globalHybridObject);
24
+ // 4. Up-cast to a HybridObject (kinda unsafe)
25
+ std::shared_ptr<HybridObject> cast = std::static_pointer_cast<HybridObject>(sharedCppPart);
26
+ return cast;
27
+ });
28
+ }
29
+
30
+ void JHybridObjectRegistry::registerNatives() {
31
+ javaClassStatic()->registerNatives({
32
+ makeNativeMethod("registerHybridObjectConstructor", JHybridObjectRegistry::registerHybridObjectConstructor),
33
+ });
34
+ }
35
+ } // namespace margelo::nitro
@@ -0,0 +1,30 @@
1
+ //
2
+ // JHybridObjectRegistry.hpp
3
+ // DoubleConversion
4
+ //
5
+ // Created by Marc Rousavy on 22.07.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "HybridObject.hpp"
11
+ #include "JHybridObjectInitializer.hpp"
12
+ #include <fbjni/fbjni.h>
13
+
14
+ namespace margelo::nitro {
15
+
16
+ using namespace facebook;
17
+
18
+ struct JHybridObjectRegistry : public jni::JavaClass<JHybridObjectRegistry> {
19
+ public:
20
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/HybridObjectRegistry;";
21
+
22
+ public:
23
+ static void registerHybridObjectConstructor(jni::alias_ref<jni::JClass> clazz, std::string hybridObjectName,
24
+ jni::alias_ref<JHybridObjectInitializer> constructorFn);
25
+
26
+ public:
27
+ static void registerNatives();
28
+ };
29
+
30
+ } // namespace margelo::nitro
@@ -0,0 +1,36 @@
1
+ //
2
+ // Created by Marc Rousavy on 21.02.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ #include <fbjni/fbjni.h>
8
+ #include <memory>
9
+
10
+ namespace margelo::nitro {
11
+
12
+ using namespace facebook;
13
+
14
+ template <typename T>
15
+ struct GlobalRefDeleter {
16
+ explicit GlobalRefDeleter(jni::global_ref<typename T::javaobject> ref) : _ref(ref) {}
17
+
18
+ void operator()(T* ptr) {
19
+ if (_ref) {
20
+ _ref.release();
21
+ }
22
+ }
23
+
24
+ private:
25
+ jni::global_ref<typename T::javaobject> _ref;
26
+ };
27
+
28
+ class JNISharedPtr {
29
+ public:
30
+ template <typename T, typename std::enable_if<std::is_base_of<jni::HybridClass<T>, T>::value>::type* = nullptr>
31
+ static std::shared_ptr<T> make_shared_from_jni(jni::global_ref<typename T::javaobject> ref) {
32
+ return std::shared_ptr<T>(ref->cthis(), GlobalRefDeleter<T>{ref});
33
+ }
34
+ };
35
+
36
+ } // namespace margelo::nitro
@@ -0,0 +1,61 @@
1
+ package com.margelo.nitro
2
+
3
+ import android.util.Log
4
+ import androidx.annotation.Keep
5
+ import com.facebook.jni.HybridData
6
+ import com.facebook.proguard.annotations.DoNotStrip
7
+
8
+ /**
9
+ * A base class for all Kotlin-based HybridObjects.
10
+ */
11
+ @Keep
12
+ @DoNotStrip
13
+ @Suppress("KotlinJniMissingFunction")
14
+ abstract class HybridObject {
15
+ /**
16
+ * Get the memory size of the Kotlin instance (plus any external heap allocations),
17
+ * in bytes.
18
+ *
19
+ * Override this to allow tracking heap allocations such as buffers or images,
20
+ * which will help the JS GC be more efficient in deleting large unused objects.
21
+ *
22
+ * @example
23
+ * ```kotlin
24
+ * val memorySize: ULong
25
+ * get() {
26
+ * val imageSize = this.bitmap.bytesPerRow * this.bitmap.height
27
+ * return getSizeOf(this) + imageSize
28
+ * }
29
+ * ```
30
+ */
31
+ @get:DoNotStrip
32
+ @get:Keep
33
+ abstract val memorySize: ULong
34
+
35
+ /**
36
+ * Contains the C++ context (Hybrid Data) for the fbjni C++ part.
37
+ */
38
+ @DoNotStrip
39
+ @Keep
40
+ val mHybridData: HybridData
41
+
42
+ init {
43
+ mHybridData = initHybrid()
44
+ }
45
+
46
+ private external fun initHybrid(): HybridData
47
+
48
+ companion object {
49
+ private const val TAG = "HybridObject"
50
+ init {
51
+ try {
52
+ Log.i(TAG, "Loading NitroModules C++ library...")
53
+ System.loadLibrary("NitroModules")
54
+ Log.i(TAG, "Successfully loaded NitroModules C++ library!")
55
+ } catch (e: Error) {
56
+ Log.e(TAG, "Failed to load NitroModules C++ library! Is it properly installed and linked?", e)
57
+ throw e
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,13 @@
1
+ package com.margelo.nitro;
2
+
3
+ import androidx.annotation.Keep;
4
+
5
+ import com.facebook.proguard.annotations.DoNotStrip;
6
+
7
+ @Keep
8
+ @DoNotStrip
9
+ public interface HybridObjectInitializer {
10
+ @Keep
11
+ @DoNotStrip
12
+ HybridObject initialize();
13
+ }
@@ -0,0 +1,30 @@
1
+ package com.margelo.nitro;
2
+
3
+ import android.util.Log;
4
+
5
+ import java.util.function.Supplier;
6
+
7
+ /**
8
+ * A registry that holds initializers for HybridObjects.
9
+ * This will be used to initialize them from JS using `NitroModules.get<T>(name)`.
10
+ * @noinspection JavaJniMissingFunction
11
+ */
12
+ public class HybridObjectRegistry {
13
+ /**
14
+ * Registers the given HybridObject in the `HybridObjectRegistry`.
15
+ * It will be uniquely identified via it's `hybridObjectName`, and can be initialized from
16
+ * JS using `NitroModules.get<T>(name)` - which will call the `constructorFn` here.
17
+ */
18
+ public static native void registerHybridObjectConstructor(String hybridObjectName, HybridObjectInitializer initializer);
19
+
20
+ private static final String TAG = "HybridObjectRegistry";
21
+ static {
22
+ Log.i(TAG, "Loading native NitroModules C++ library...");
23
+ try {
24
+ System.loadLibrary("NitroModules");
25
+ Log.i(TAG, "Successfully loaded native NitroModules C++ library!");
26
+ } catch (Throwable e) {
27
+ Log.e(TAG, "Failed to load native NitroModules C++ library!", e);
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,26 @@
1
+ package com.margelo.nitro;
2
+
3
+ import androidx.annotation.Nullable;
4
+
5
+ import com.facebook.react.bridge.NativeModule;
6
+ import com.facebook.react.bridge.ReactApplicationContext;
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider;
8
+ import com.facebook.react.TurboReactPackage;
9
+
10
+ import java.util.HashMap;
11
+
12
+ public class NitroModulesPackage extends TurboReactPackage {
13
+
14
+ @Nullable
15
+ @Override
16
+ public NativeModule getModule(String name, ReactApplicationContext reactContext) {
17
+ return null;
18
+ }
19
+
20
+ @Override
21
+ public ReactModuleInfoProvider getReactModuleInfoProvider() {
22
+ return () -> {
23
+ return new HashMap<>();
24
+ };
25
+ }
26
+ }
@@ -4,6 +4,7 @@
4
4
 
5
5
  #pragma once
6
6
 
7
+ #include "CountTrailingOptionals.hpp"
7
8
  #include "HybridContext.hpp"
8
9
  #include "JSIConverter.hpp"
9
10
  #include "NitroLogger.hpp"
@@ -67,7 +68,8 @@ public:
67
68
  * Get the `std::shared_ptr` instance of this HybridObject.
68
69
  * The HybridObject must be managed inside a `shared_ptr` already, otherwise this will fail.
69
70
  */
70
- template <typename Derived> std::shared_ptr<Derived> shared() {
71
+ template <typename Derived>
72
+ std::shared_ptr<Derived> shared() {
71
73
  return std::static_pointer_cast<Derived>(shared_from_this());
72
74
  }
73
75
 
@@ -151,14 +153,16 @@ private:
151
153
  private:
152
154
  template <typename Derived, typename ReturnType, typename... Args, size_t... Is>
153
155
  static inline jsi::Value callMethod(Derived* obj, ReturnType (Derived::*method)(Args...), jsi::Runtime& runtime, const jsi::Value* args,
154
- std::index_sequence<Is...>) {
156
+ size_t argsSize, std::index_sequence<Is...>) {
157
+ jsi::Value defaultValue;
158
+
155
159
  if constexpr (std::is_void_v<ReturnType>) {
156
160
  // It's a void method.
157
- (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
161
+ (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, Is < argsSize ? args[Is] : defaultValue)...);
158
162
  return jsi::Value::undefined();
159
163
  } else {
160
164
  // It's returning some C++ type, we need to convert that to a JSI value now.
161
- ReturnType result = (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
165
+ ReturnType result = (obj->*method)(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, Is < argsSize ? args[Is] : defaultValue)...);
162
166
  return JSIConverter<std::decay_t<ReturnType>>::toJSI(runtime, std::move(result));
163
167
  }
164
168
  }
@@ -168,11 +172,22 @@ private:
168
172
  MethodType type) {
169
173
  return [name, derivedInstance, method, type](jsi::Runtime& runtime, const jsi::Value& thisVal, const jsi::Value* args,
170
174
  size_t count) -> jsi::Value {
171
- if (count != sizeof...(Args)) {
175
+ constexpr size_t optionalArgsCount = trailing_optionals_count_v<Args...>;
176
+ constexpr size_t maxArgsCount = sizeof...(Args);
177
+ constexpr size_t minArgsCount = maxArgsCount - optionalArgsCount;
178
+ bool isWithinArgsRange = (count >= minArgsCount && count <= maxArgsCount);
179
+ if (!isWithinArgsRange) {
172
180
  // invalid amount of arguments passed!
173
181
  std::string hybridObjectName = derivedInstance->_name;
174
- throw jsi::JSError(runtime, hybridObjectName + "." + name + "(...) expected " + std::to_string(sizeof...(Args)) +
175
- " arguments, but received " + std::to_string(count) + "!");
182
+ if (minArgsCount == maxArgsCount) {
183
+ // min and max args length is the same, so we don't have any optional parameters. fixed count
184
+ throw jsi::JSError(runtime, hybridObjectName + "." + name + "(...) expected " + std::to_string(maxArgsCount) +
185
+ " arguments, but received " + std::to_string(count) + "!");
186
+ } else {
187
+ // min and max args length are different, so we have optional parameters - variable length arguments.
188
+ throw jsi::JSError(runtime, hybridObjectName + "." + name + "(...) expected between " + std::to_string(minArgsCount) + " and " +
189
+ std::to_string(maxArgsCount) + " arguments, but received " + std::to_string(count) + "!");
190
+ }
176
191
  }
177
192
 
178
193
  try {
@@ -183,7 +198,7 @@ private:
183
198
  } else {
184
199
  // Call the actual method with JSI values as arguments and return a JSI value again.
185
200
  // Internally, this method converts the JSI values to C++ values.
186
- return callMethod(derivedInstance, method, runtime, args, std::index_sequence_for<Args...>{});
201
+ return callMethod(derivedInstance, method, runtime, args, count, std::index_sequence_for<Args...>{});
187
202
  }
188
203
  } catch (const std::exception& exception) {
189
204
  std::string hybridObjectName = derivedInstance->_name;
@@ -13,7 +13,8 @@ namespace margelo::nitro {
13
13
 
14
14
  using namespace facebook;
15
15
 
16
- template <typename T> class PointerHolder final : public HybridObject {
16
+ template <typename T>
17
+ class PointerHolder final : public HybridObject {
17
18
  protected:
18
19
  // no default constructor
19
20
  PointerHolder() = delete;