react-native-nitro-modules 0.32.0-beta.0 → 0.32.1
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/NitroModules.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/cpp/core/JAnyMap.cpp +100 -2
- package/android/src/main/cpp/core/JAnyMap.hpp +26 -0
- package/android/src/main/java/com/margelo/nitro/core/AnyMap.kt +21 -21
- package/android/src/main/java/com/margelo/nitro/core/ArrayBuffer.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/core/HybridObject.kt +3 -3
- package/cpp/core/AnyMap.cpp +1 -1
- package/cpp/core/AnyMap.hpp +1 -1
- package/cpp/core/ArrayBuffer.hpp +3 -3
- package/cpp/core/BoxedHybridObject.cpp +2 -1
- package/cpp/core/HybridFunction.hpp +2 -1
- package/cpp/core/HybridObject.cpp +1 -1
- package/cpp/core/HybridObject.hpp +4 -4
- package/cpp/entrypoint/HybridNitroModulesProxy.cpp +1 -1
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +2 -2
- package/cpp/jsi/JSIConverter+NativeState.hpp +1 -1
- package/cpp/prototype/HybridObjectPrototype.cpp +31 -17
- package/cpp/prototype/HybridObjectPrototype.hpp +6 -12
- package/cpp/prototype/PrototypeChain.hpp +1 -1
- package/cpp/registry/HybridObjectRegistry.hpp +1 -1
- package/cpp/threading/CallInvokerDispatcher.hpp +1 -1
- package/cpp/utils/CommonGlobals.cpp +1 -1
- package/cpp/utils/JSCallback.hpp +1 -1
- package/cpp/utils/NitroDefines.hpp +1 -1
- package/cpp/utils/PropNameIDCache.cpp +1 -1
- package/cpp/utils/PropNameIDCache.hpp +4 -3
- package/ios/core/HybridObject.swift +1 -1
- package/ios/utils/SwiftClosure.swift +1 -1
- package/package.json +1 -1
package/NitroModules.podspec
CHANGED
|
@@ -64,7 +64,7 @@ Pod::Spec.new do |s|
|
|
|
64
64
|
xcconfig = {
|
|
65
65
|
# Use C++ 20
|
|
66
66
|
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
|
|
67
|
-
# Enables C++ <-> Swift interop (by default
|
|
67
|
+
# Enables C++ <-> Swift interop (by default its only ObjC)
|
|
68
68
|
"SWIFT_OBJC_INTEROP_MODE" => "objcxx",
|
|
69
69
|
# Enables stricter modular headers
|
|
70
70
|
"DEFINES_MODULE" => "YES",
|
package/android/build.gradle
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
// Created by Marc Rousavy on 14.07.24.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
8
|
#include "JAnyMap.hpp"
|
|
11
9
|
|
|
12
10
|
namespace margelo::nitro {
|
|
@@ -117,4 +115,104 @@ jni::local_ref<jni::JHashMap<jni::JString, jni::JObject>> JAnyMap::anyObjectToJH
|
|
|
117
115
|
return jMap;
|
|
118
116
|
}
|
|
119
117
|
|
|
118
|
+
AnyValue JAnyMap::jObjectToAnyValue(jni::alias_ref<jni::JObject> jObject) {
|
|
119
|
+
if (!jObject) {
|
|
120
|
+
return nitro::null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check for Double
|
|
124
|
+
if (jObject->isInstanceOf(jni::JDouble::javaClassStatic())) {
|
|
125
|
+
auto jDouble = jni::static_ref_cast<jni::JDouble>(jObject);
|
|
126
|
+
return jDouble->doubleValue();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check for Float
|
|
130
|
+
if (jObject->isInstanceOf(jni::JFloat::javaClassStatic())) {
|
|
131
|
+
auto jFloat = jni::static_ref_cast<jni::JFloat>(jObject);
|
|
132
|
+
return static_cast<double>(jFloat->floatValue());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check for Integer
|
|
136
|
+
if (jObject->isInstanceOf(jni::JInteger::javaClassStatic())) {
|
|
137
|
+
auto jInt = jni::static_ref_cast<jni::JInteger>(jObject);
|
|
138
|
+
return static_cast<double>(jInt->intValue());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check for Boolean
|
|
142
|
+
if (jObject->isInstanceOf(jni::JBoolean::javaClassStatic())) {
|
|
143
|
+
auto jBool = jni::static_ref_cast<jni::JBoolean>(jObject);
|
|
144
|
+
return jBool->booleanValue();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check for Long (bigint)
|
|
148
|
+
if (jObject->isInstanceOf(jni::JLong::javaClassStatic())) {
|
|
149
|
+
auto jLong = jni::static_ref_cast<jni::JLong>(jObject);
|
|
150
|
+
return jLong->longValue();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check for String
|
|
154
|
+
if (jObject->isInstanceOf(jni::JString::javaClassStatic())) {
|
|
155
|
+
auto jString = jni::static_ref_cast<jni::JString>(jObject);
|
|
156
|
+
return jString->toStdString();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check for List (array)
|
|
160
|
+
if (jObject->isInstanceOf(jni::JList<jni::JObject>::javaClassStatic())) {
|
|
161
|
+
auto jList = jni::static_ref_cast<jni::JList<jni::JObject>>(jObject);
|
|
162
|
+
return jListToAnyArray(jList);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check for Map (object)
|
|
166
|
+
if (jObject->isInstanceOf(jni::JMap<jni::JString, jni::JObject>::javaClassStatic())) {
|
|
167
|
+
auto jMap = jni::static_ref_cast<jni::JMap<jni::JString, jni::JObject>>(jObject);
|
|
168
|
+
return jHashMapToAnyObject(jMap);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
throw std::runtime_error("Cannot convert JObject to AnyValue - unsupported type!");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
AnyArray JAnyMap::jListToAnyArray(jni::alias_ref<jni::JList<jni::JObject>> jList) {
|
|
175
|
+
AnyArray array;
|
|
176
|
+
array.reserve(jList->size());
|
|
177
|
+
for (const auto& item : *jList) {
|
|
178
|
+
array.push_back(jObjectToAnyValue(item));
|
|
179
|
+
}
|
|
180
|
+
return array;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
AnyObject JAnyMap::jHashMapToAnyObject(jni::alias_ref<jni::JMap<jni::JString, jni::JObject>> jMap) {
|
|
184
|
+
AnyObject object;
|
|
185
|
+
object.reserve(jMap->size());
|
|
186
|
+
for (const auto& entry : *jMap) {
|
|
187
|
+
object.emplace(entry.first->toStdString(), jObjectToAnyValue(entry.second));
|
|
188
|
+
}
|
|
189
|
+
return object;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
jni::local_ref<JAnyMap::javaobject> JAnyMap::fromMap(jni::alias_ref<jclass>, jni::alias_ref<jni::JMap<jni::JString, jni::JObject>> javaMap,
|
|
193
|
+
bool ignoreIncompatible) {
|
|
194
|
+
size_t size = javaMap->size();
|
|
195
|
+
jni::local_ref<JAnyMap::javaobject> anyMap = JAnyMap::create(size);
|
|
196
|
+
|
|
197
|
+
auto& map = anyMap->cthis()->_map->getMap();
|
|
198
|
+
|
|
199
|
+
// Bulk convert all entries from Java to C++
|
|
200
|
+
for (const auto& entry : *javaMap) {
|
|
201
|
+
try {
|
|
202
|
+
std::string key = entry.first->toStdString();
|
|
203
|
+
AnyValue value = jObjectToAnyValue(entry.second);
|
|
204
|
+
map.emplace(std::move(key), std::move(value));
|
|
205
|
+
} catch (...) {
|
|
206
|
+
if (ignoreIncompatible) {
|
|
207
|
+
// encountered an incompatible key. Ignore it.
|
|
208
|
+
} else {
|
|
209
|
+
// encountered an incompatible key - we now throw!
|
|
210
|
+
throw;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return anyMap;
|
|
216
|
+
}
|
|
217
|
+
|
|
120
218
|
} // namespace margelo::nitro
|
|
@@ -41,6 +41,18 @@ public:
|
|
|
41
41
|
static jni::local_ref<JAnyMap::javaobject> create(const std::shared_ptr<AnyMap>& map) {
|
|
42
42
|
return newObjectCxxArgs(map);
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a new `JAnyMap` from the given preallocated size.
|
|
46
|
+
*/
|
|
47
|
+
static jni::local_ref<JAnyMap::javaobject> create(int preallocatedSize) {
|
|
48
|
+
return newObjectCxxArgs(preallocatedSize);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a new empty `JAnyMap`.
|
|
52
|
+
*/
|
|
53
|
+
static jni::local_ref<JAnyMap::javaobject> create() {
|
|
54
|
+
return newObjectCxxArgs();
|
|
55
|
+
}
|
|
44
56
|
|
|
45
57
|
private:
|
|
46
58
|
JAnyMap() {
|
|
@@ -133,11 +145,24 @@ protected:
|
|
|
133
145
|
*/
|
|
134
146
|
jni::local_ref<jni::JHashMap<jni::JString, jni::JObject>> toHashMap();
|
|
135
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Bulk-converts a Java `Map<String, Object>` into this `JAnyMap` in a single JNI call.
|
|
150
|
+
*
|
|
151
|
+
* When `ignoreIncompatible` is `true`, this will drop keys that can't be converted.
|
|
152
|
+
* When `ignoreIncompatible` is `false`, this will throw when a key cannot be converted.
|
|
153
|
+
*/
|
|
154
|
+
static jni::local_ref<JAnyMap::javaobject> fromMap(jni::alias_ref<jclass>, jni::alias_ref<jni::JMap<jni::JString, jni::JObject>> javaMap,
|
|
155
|
+
bool ignoreIncompatible);
|
|
156
|
+
|
|
136
157
|
private:
|
|
137
158
|
static jni::local_ref<jni::JObject> anyValueToJObject(const AnyValue& value);
|
|
138
159
|
static jni::local_ref<jni::JArrayList<jni::JObject>> anyArrayToJList(const AnyArray& array);
|
|
139
160
|
static jni::local_ref<jni::JHashMap<jni::JString, jni::JObject>> anyObjectToJHashMap(const AnyObject& object);
|
|
140
161
|
|
|
162
|
+
static AnyValue jObjectToAnyValue(jni::alias_ref<jni::JObject> jObject);
|
|
163
|
+
static AnyArray jListToAnyArray(jni::alias_ref<jni::JList<jni::JObject>> jList);
|
|
164
|
+
static AnyObject jHashMapToAnyObject(jni::alias_ref<jni::JMap<jni::JString, jni::JObject>> jMap);
|
|
165
|
+
|
|
141
166
|
public:
|
|
142
167
|
[[nodiscard]]
|
|
143
168
|
std::shared_ptr<AnyMap> getMap() const {
|
|
@@ -189,6 +214,7 @@ public:
|
|
|
189
214
|
makeNativeMethod("merge", JAnyMap::merge),
|
|
190
215
|
// bulk conversion
|
|
191
216
|
makeNativeMethod("toHashMap", JAnyMap::toHashMap),
|
|
217
|
+
makeNativeMethod("fromMap", JAnyMap::fromMap),
|
|
192
218
|
});
|
|
193
219
|
}
|
|
194
220
|
};
|
|
@@ -39,29 +39,26 @@ class AnyMap {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
companion object {
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Converts the given [map] to a new [AnyMap].
|
|
44
|
+
* @param map The map of keys/value types. Only a number of value types
|
|
45
|
+
* are supported in [AnyMap] - see Nitro docs for more information.
|
|
46
|
+
* @param ignoreIncompatible Whether to throw when an incompatible
|
|
47
|
+
* type is found, or not. If this is `false`, all incompatible key/value
|
|
48
|
+
* pairs will just be ignored.
|
|
49
|
+
*/
|
|
50
|
+
@JvmStatic
|
|
51
|
+
external fun fromMap(
|
|
43
52
|
map: Map<String, Any?>,
|
|
44
|
-
ignoreIncompatible: Boolean
|
|
45
|
-
): AnyMap
|
|
46
|
-
val anyMap = AnyMap(map.size)
|
|
47
|
-
for ((key, value) in map) {
|
|
48
|
-
try {
|
|
49
|
-
anyMap.setAny(key, value)
|
|
50
|
-
} catch (error: Throwable) {
|
|
51
|
-
if (ignoreIncompatible) {
|
|
52
|
-
continue
|
|
53
|
-
} else {
|
|
54
|
-
throw error
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return anyMap
|
|
59
|
-
}
|
|
53
|
+
ignoreIncompatible: Boolean,
|
|
54
|
+
): AnyMap
|
|
60
55
|
}
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Converts this [AnyMap] to a new [HashMap] by
|
|
59
|
+
* copying each key/value.
|
|
60
|
+
*/
|
|
61
|
+
external fun toHashMap(): HashMap<String, Any?>
|
|
65
62
|
|
|
66
63
|
fun setAny(
|
|
67
64
|
key: String,
|
|
@@ -74,7 +71,10 @@ class AnyMap {
|
|
|
74
71
|
return getAnyValue(key).toAny()
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
external fun
|
|
74
|
+
private external fun fromHashMap(
|
|
75
|
+
map: Map<String, Any?>,
|
|
76
|
+
ignoreIncompatible: Boolean,
|
|
77
|
+
)
|
|
78
78
|
|
|
79
79
|
@FastNative
|
|
80
80
|
external fun contains(key: String): Boolean
|
|
@@ -43,7 +43,7 @@ class ArrayBuffer {
|
|
|
43
43
|
* - If the `ArrayBuffer` holds a `ByteBuffer`, `getBuffer(false)` can safely be called to
|
|
44
44
|
* get shared access to the underlying data, without performing any copies.
|
|
45
45
|
* - If the `ArrayBuffer` doesn't hold a `ByteBuffer`, it can still be accessed via `getBuffer(false)`,
|
|
46
|
-
* but the returned `ByteBuffer` is only valid as long as
|
|
46
|
+
* but the returned `ByteBuffer` is only valid as long as its parent `ArrayBuffer` is alive.
|
|
47
47
|
*/
|
|
48
48
|
val isByteBuffer: Boolean
|
|
49
49
|
get() = getIsByteBuffer()
|
|
@@ -104,7 +104,7 @@ class ArrayBuffer {
|
|
|
104
104
|
// The ByteBuffer is 1:1 mapped to a byte array - return as is!
|
|
105
105
|
return array
|
|
106
106
|
}
|
|
107
|
-
// we had a CPU-backed array, but
|
|
107
|
+
// we had a CPU-backed array, but its size differs from our ArrayBuffer size.
|
|
108
108
|
// This might be because the ArrayBuffer has a smaller view of the data, so we need
|
|
109
109
|
// to resort back to a good ol' copy.
|
|
110
110
|
}
|
|
@@ -35,7 +35,7 @@ abstract class HybridObject {
|
|
|
35
35
|
* Eagerly- (and manually-) dispose all native resources this `HybridObject` holds.
|
|
36
36
|
* This method can only be manually called from JS using `dispose()`.
|
|
37
37
|
*
|
|
38
|
-
* If this method is never manually called, a `HybridObject` is expected to disposes
|
|
38
|
+
* If this method is never manually called, a `HybridObject` is expected to disposes its
|
|
39
39
|
* resources as usual via the object's destructor (`~HybridObject()`, `deinit` or `finalize()`).
|
|
40
40
|
*
|
|
41
41
|
* By default, this method does nothing. It can be overridden to perform actual disposing/cleanup
|
|
@@ -58,13 +58,13 @@ abstract class HybridObject {
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Holds the native C++ instance.
|
|
61
|
-
* In `HybridObject`, the C++ instance is a sub-class of `JHybridObject`, such as one of
|
|
61
|
+
* In `HybridObject`, the C++ instance is a sub-class of `JHybridObject`, such as one of its specs.
|
|
62
62
|
* This is `null`, until `updateNative(..)` is called.
|
|
63
63
|
*/
|
|
64
64
|
private var mHybridData: HybridData? = null
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* If `HybridObject` is subclassed, the sub-class needs to create
|
|
67
|
+
* If `HybridObject` is subclassed, the sub-class needs to create its own `HybridData`
|
|
68
68
|
* with a C++ `jni::HybridClass` representing the subclass directly.
|
|
69
69
|
* Then, that `HybridData` must be passed upwards to `HybridObject` using `updateNative(..)`.
|
|
70
70
|
*
|
package/cpp/core/AnyMap.cpp
CHANGED
|
@@ -189,7 +189,7 @@ void AnyMap::setAny(const std::string& key, const AnyValue& value) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// C++ getter
|
|
192
|
-
|
|
192
|
+
std::unordered_map<std::string, AnyValue>& AnyMap::getMap() {
|
|
193
193
|
return _map;
|
|
194
194
|
}
|
|
195
195
|
|
package/cpp/core/AnyMap.hpp
CHANGED
package/cpp/core/ArrayBuffer.hpp
CHANGED
|
@@ -28,7 +28,7 @@ using DeleteFn = std::function<void()>;
|
|
|
28
28
|
* - `NativeArrayBuffer`: Created from native (C++), and can either own the memory (`isOwner()`), or borrow it.
|
|
29
29
|
* - `JSArrayBuffer`: Received from JS, and will only be alive for as long as the JS Runtime is actually alive.
|
|
30
30
|
*
|
|
31
|
-
* Also, an `ArrayBuffer` can either own
|
|
31
|
+
* Also, an `ArrayBuffer` can either own its memory, or just borrow its memory.
|
|
32
32
|
* - Owning = the `ArrayBuffer`'s `data()` is alive as long as the `ArrayBuffer` is alive.
|
|
33
33
|
* When this `ArrayBuffer` gets deleted, it will free the memory.
|
|
34
34
|
* - Borrowed = the `ArrayBuffer`'s `data()` might be deleted at any point from an external source (e.g. the JS garbage collector).
|
|
@@ -116,13 +116,13 @@ private:
|
|
|
116
116
|
/**
|
|
117
117
|
* Represents a JS-based `ArrayBuffer`.
|
|
118
118
|
*
|
|
119
|
-
* While
|
|
119
|
+
* While its underlying data might have been allocated on the native side (`NativeArrayBuffer`),
|
|
120
120
|
* we only have a JS reference to the `ArrayBuffer` object so it is considered a "borrowed"-resource.
|
|
121
121
|
*
|
|
122
122
|
* `data()` and `size()` can only be accessed synchronously on the JS Runtime Thread.
|
|
123
123
|
* If you want to access it elsewhere, copy the buffer first.
|
|
124
124
|
*
|
|
125
|
-
* If the JS ArrayBuffer (or
|
|
125
|
+
* If the JS ArrayBuffer (or its JS Runtime) have already been deleted, `data()` returns `nullptr`.
|
|
126
126
|
*/
|
|
127
127
|
class JSArrayBuffer final : public ArrayBuffer {
|
|
128
128
|
public:
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#include "BoxedHybridObject.hpp"
|
|
9
|
+
#include "PropNameIDCache.hpp"
|
|
9
10
|
|
|
10
11
|
namespace margelo::nitro {
|
|
11
12
|
|
|
@@ -18,7 +19,7 @@ jsi::Value BoxedHybridObject::get(jsi::Runtime& runtime, const jsi::PropNameID&
|
|
|
18
19
|
|
|
19
20
|
if (name == "unbox") {
|
|
20
21
|
return jsi::Function::createFromHostFunction(
|
|
21
|
-
runtime,
|
|
22
|
+
runtime, PropNameIDCache::get(runtime, "unbox"), 0,
|
|
22
23
|
[hybridObject = _hybridObject](jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value {
|
|
23
24
|
return hybridObject->toObject(runtime);
|
|
24
25
|
});
|
|
@@ -17,6 +17,7 @@ struct JSIConverter;
|
|
|
17
17
|
#include "JSIConverter.hpp"
|
|
18
18
|
#include "NitroDefines.hpp"
|
|
19
19
|
#include "NitroTypeInfo.hpp"
|
|
20
|
+
#include "PropNameIDCache.hpp"
|
|
20
21
|
#include <exception>
|
|
21
22
|
#include <functional>
|
|
22
23
|
#include <jsi/jsi.h>
|
|
@@ -67,7 +68,7 @@ public:
|
|
|
67
68
|
public:
|
|
68
69
|
// functions
|
|
69
70
|
inline jsi::Function toJSFunction(jsi::Runtime& runtime) const {
|
|
70
|
-
return jsi::Function::createFromHostFunction(runtime,
|
|
71
|
+
return jsi::Function::createFromHostFunction(runtime, PropNameIDCache::get(runtime, _name), static_cast<unsigned int>(_paramCount),
|
|
71
72
|
_function);
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -91,7 +91,7 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
|
|
|
91
91
|
// .configurable has to be true because this property is non-frozen
|
|
92
92
|
.configurable = true,
|
|
93
93
|
.enumerable = true,
|
|
94
|
-
.value = jsi::String::
|
|
94
|
+
.value = jsi::String::createFromAscii(runtime, typeName),
|
|
95
95
|
.writable = false,
|
|
96
96
|
});
|
|
97
97
|
#endif
|
|
@@ -60,14 +60,14 @@ public:
|
|
|
60
60
|
/**
|
|
61
61
|
* Return the `jsi::Object` that holds this `HybridObject`. (boxed in a `jsi::Value`)
|
|
62
62
|
* This properly assigns (or creates) the base prototype for this type,
|
|
63
|
-
* and assigns
|
|
63
|
+
* and assigns its NativeState.
|
|
64
64
|
* Additionally, this sets the external memory pressure for proper GC memory management.
|
|
65
65
|
*/
|
|
66
66
|
jsi::Value toObject(jsi::Runtime& runtime);
|
|
67
67
|
|
|
68
68
|
public:
|
|
69
69
|
/**
|
|
70
|
-
* Get the `std::shared_ptr` instance of this HybridObject as
|
|
70
|
+
* Get the `std::shared_ptr` instance of this HybridObject as its concrete type.
|
|
71
71
|
* The HybridObject must be managed inside a `shared_ptr` already, otherwise this will fail.
|
|
72
72
|
*/
|
|
73
73
|
template <typename Derived>
|
|
@@ -90,7 +90,7 @@ public:
|
|
|
90
90
|
* While two `jsi::Object`s of the same `HybridObject` might not be equal when compared with `==`,
|
|
91
91
|
* they might still be the same `HybridObject` - in this case `equals(other)` will return true.
|
|
92
92
|
*/
|
|
93
|
-
bool equals(const std::shared_ptr<HybridObject>& other);
|
|
93
|
+
virtual bool equals(const std::shared_ptr<HybridObject>& other);
|
|
94
94
|
/**
|
|
95
95
|
* Get a string representation of this `HybridObject` - useful for logging or debugging.
|
|
96
96
|
*/
|
|
@@ -99,7 +99,7 @@ public:
|
|
|
99
99
|
* Eagerly- (and manually-) dispose all native resources this `HybridObject` holds.
|
|
100
100
|
* This method can only be manually called from JS using `dispose()`.
|
|
101
101
|
*
|
|
102
|
-
* If this method is never manually called, a `HybridObject` is expected to disposes
|
|
102
|
+
* If this method is never manually called, a `HybridObject` is expected to disposes its
|
|
103
103
|
* resources as usual via the object's destructor (`~HybridObject()`, `deinit` or `finalize()`).
|
|
104
104
|
*
|
|
105
105
|
* By default, this method does nothing. It can be overridden to perform actual disposing/cleanup
|
|
@@ -71,7 +71,7 @@ jsi::Value HybridNitroModulesProxy::isHybridObject(jsi::Runtime& runtime, const
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
std::shared_ptr<HybridObject> HybridNitroModulesProxy::updateMemorySize(const std::shared_ptr<HybridObject>& hybridObject) {
|
|
74
|
-
// If a hybridObject goes from Native -> JS, it will update
|
|
74
|
+
// If a hybridObject goes from Native -> JS, it will update its memory size internally (in `HybridObject::toObject(..)`).
|
|
75
75
|
// This is all that function does.
|
|
76
76
|
return hybridObject;
|
|
77
77
|
}
|
|
@@ -50,7 +50,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer
|
|
|
50
50
|
if (!object.isArrayBuffer(runtime)) [[unlikely]] {
|
|
51
51
|
throw std::invalid_argument("Object \"" + arg.toString(runtime).utf8(runtime) +
|
|
52
52
|
"\" is not an ArrayBuffer! "
|
|
53
|
-
"Are you maybe passing a TypedArray (e.g. Uint8Array)? Try to pass
|
|
53
|
+
"Are you maybe passing a TypedArray (e.g. Uint8Array)? Try to pass its `.buffer` value.");
|
|
54
54
|
}
|
|
55
55
|
#endif
|
|
56
56
|
if (object.hasNativeState<MutableBufferNativeState>(runtime)) {
|
|
@@ -69,7 +69,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer
|
|
|
69
69
|
}
|
|
70
70
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<jsi::MutableBuffer>& buffer) {
|
|
71
71
|
if (auto jsBuffer = std::dynamic_pointer_cast<JSArrayBuffer>(buffer)) {
|
|
72
|
-
// It already is a JSBuffer! Let's try to just get
|
|
72
|
+
// It already is a JSBuffer! Let's try to just get its existing jsi::Value...
|
|
73
73
|
auto jsValue = jsBuffer->getJSReference();
|
|
74
74
|
if (jsValue != nullptr) [[likely]] {
|
|
75
75
|
return jsi::Value(runtime, *jsValue);
|
|
@@ -65,7 +65,7 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
if constexpr (std::is_base_of_v<HybridObject, TPointee>) {
|
|
68
|
-
// It's a HybridObject - use
|
|
68
|
+
// It's a HybridObject - use its internal constructor which caches jsi::Objects for proper memory management!
|
|
69
69
|
return arg->toObject(runtime);
|
|
70
70
|
} else {
|
|
71
71
|
// It's any other kind of jsi::NativeState - just create it as normal. This will not have a prototype then!
|
|
@@ -55,25 +55,24 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// 5. Add all properties (getter + setter) to it using defineProperty
|
|
58
|
-
for (const auto& getter : prototype->getGetters()) {
|
|
59
|
-
const auto&
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
for (const auto& [name, getter] : prototype->getGetters()) {
|
|
59
|
+
const auto& setterIterator = prototype->getSetters().find(name);
|
|
60
|
+
if (setterIterator != prototype->getSetters().end()) {
|
|
61
|
+
// get + set
|
|
62
|
+
const HybridFunction& setter = setterIterator->second;
|
|
63
|
+
CommonGlobals::Object::defineProperty(runtime, object, name.c_str(),
|
|
64
|
+
ComputedPropertyDescriptor{// getter + setter
|
|
65
|
+
.configurable = false,
|
|
66
|
+
.enumerable = true,
|
|
67
|
+
.get = getter.toJSFunction(runtime),
|
|
68
|
+
.set = setter.toJSFunction(runtime)});
|
|
69
|
+
} else {
|
|
63
70
|
// get
|
|
64
71
|
CommonGlobals::Object::defineProperty(runtime, object, name.c_str(),
|
|
65
|
-
ComputedReadonlyPropertyDescriptor{//
|
|
72
|
+
ComputedReadonlyPropertyDescriptor{// getter
|
|
66
73
|
.configurable = false,
|
|
67
74
|
.enumerable = true,
|
|
68
|
-
.get = getter.
|
|
69
|
-
} else {
|
|
70
|
-
// get + set
|
|
71
|
-
CommonGlobals::Object::defineProperty(runtime, object, name.c_str(),
|
|
72
|
-
ComputedPropertyDescriptor{// readonly with setter
|
|
73
|
-
.configurable = false,
|
|
74
|
-
.enumerable = false,
|
|
75
|
-
.get = getter.second.toJSFunction(runtime),
|
|
76
|
-
.set = setter->second.toJSFunction(runtime)});
|
|
75
|
+
.get = getter.toJSFunction(runtime)});
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
78
|
|
|
@@ -84,7 +83,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
84
83
|
PlainPropertyDescriptor{
|
|
85
84
|
.configurable = false,
|
|
86
85
|
.enumerable = true,
|
|
87
|
-
.value = jsi::String::
|
|
86
|
+
.value = jsi::String::createFromAscii(runtime, prototypeName),
|
|
88
87
|
.writable = false,
|
|
89
88
|
});
|
|
90
89
|
#endif
|
|
@@ -97,13 +96,28 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
97
96
|
// 8. Throw it into our cache so the next lookup can be cached and therefore faster
|
|
98
97
|
JSICacheReference jsiCache = JSICache::getOrCreateCache(runtime);
|
|
99
98
|
BorrowingReference<jsi::Object> sharedObject = jsiCache.makeShared(std::move(object));
|
|
100
|
-
|
|
99
|
+
const NativeInstanceId& instanceId = prototype->getNativeInstanceId();
|
|
101
100
|
prototypeCache[instanceId] = sharedObject;
|
|
102
101
|
|
|
103
102
|
// 9. Return it!
|
|
104
103
|
return jsi::Value(runtime, *sharedObject);
|
|
105
104
|
}
|
|
106
105
|
|
|
106
|
+
void HybridObjectPrototype::ensureInitialized() {
|
|
107
|
+
if (!_didLoadMethods) {
|
|
108
|
+
// lock in case we try to create `HybridObject`s in parallel Runtimes
|
|
109
|
+
static std::mutex mutex;
|
|
110
|
+
std::unique_lock lock(mutex);
|
|
111
|
+
if (_didLoadMethods) [[unlikely]] {
|
|
112
|
+
// another call to `ensureInitialized()` has initialized in the meantime. abort.
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// lazy-load all exposed methods
|
|
116
|
+
loadHybridMethods();
|
|
117
|
+
_didLoadMethods = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
107
121
|
jsi::Value HybridObjectPrototype::getPrototype(jsi::Runtime& runtime) {
|
|
108
122
|
ensureInitialized();
|
|
109
123
|
|
|
@@ -30,11 +30,6 @@ using namespace facebook;
|
|
|
30
30
|
* as long as it has a valid NativeState (`this`).
|
|
31
31
|
*/
|
|
32
32
|
class HybridObjectPrototype {
|
|
33
|
-
private:
|
|
34
|
-
PrototypeChain _prototypeChain;
|
|
35
|
-
bool _didLoadMethods = false;
|
|
36
|
-
static constexpr auto TAG = "HybridObjectPrototype";
|
|
37
|
-
|
|
38
33
|
public:
|
|
39
34
|
HybridObjectPrototype() {}
|
|
40
35
|
|
|
@@ -62,13 +57,12 @@ private:
|
|
|
62
57
|
/**
|
|
63
58
|
* Ensures that all Hybrid Methods, Getters and Setters are initialized by calling loadHybridMethods().
|
|
64
59
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
60
|
+
void ensureInitialized();
|
|
61
|
+
|
|
62
|
+
private:
|
|
63
|
+
PrototypeChain _prototypeChain;
|
|
64
|
+
volatile bool _didLoadMethods = false;
|
|
65
|
+
static constexpr auto TAG = "HybridObjectPrototype";
|
|
72
66
|
|
|
73
67
|
protected:
|
|
74
68
|
using RegisterFn = void (*NON_NULL)(Prototype&);
|
|
@@ -63,7 +63,7 @@ private:
|
|
|
63
63
|
return node;
|
|
64
64
|
} else {
|
|
65
65
|
if (node->hasBase()) {
|
|
66
|
-
// We didn't find a match in this prototype, let's recursively try
|
|
66
|
+
// We didn't find a match in this prototype, let's recursively try its parent!
|
|
67
67
|
return getOrExtendPrototype<Derived>(node->getBase());
|
|
68
68
|
} else {
|
|
69
69
|
// We didn't find `Derived` and we don't have a base- add a child and shift the tree by one.
|
|
@@ -27,7 +27,7 @@ public:
|
|
|
27
27
|
public:
|
|
28
28
|
/**
|
|
29
29
|
* Registers the given HybridObject in the `HybridObjectRegistry`.
|
|
30
|
-
* It will be uniquely identified via
|
|
30
|
+
* It will be uniquely identified via its `hybridObjectName`, and can be initialized from
|
|
31
31
|
* JS using `NitroModules.createHybridObject<T>(name)` - which will call the `constructorFn` here.
|
|
32
32
|
*/
|
|
33
33
|
static void registerHybridObjectConstructor(const std::string& hybridObjectName, HybridObjectConstructorFn&& constructorFn);
|
|
@@ -15,7 +15,7 @@ namespace margelo::nitro {
|
|
|
15
15
|
using namespace facebook;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* A Dispatcher that uses react::CallInvoker for
|
|
18
|
+
* A Dispatcher that uses react::CallInvoker for its implementation
|
|
19
19
|
*/
|
|
20
20
|
class CallInvokerDispatcher final : public Dispatcher {
|
|
21
21
|
public:
|
|
@@ -120,7 +120,7 @@ jsi::Value CommonGlobals::Promise::callConstructor(jsi::Runtime& runtime, jsi::H
|
|
|
120
120
|
const jsi::Function& promiseCtor = getGlobalFunction(
|
|
121
121
|
runtime, "Promise", [](jsi::Runtime& runtime) { return runtime.global().getPropertyAsFunction(runtime, "Promise"); });
|
|
122
122
|
jsi::Function executorFunc =
|
|
123
|
-
jsi::Function::createFromHostFunction(runtime,
|
|
123
|
+
jsi::Function::createFromHostFunction(runtime, PropNameIDCache::get(runtime, "executor"), 2, std::move(executor));
|
|
124
124
|
return promiseCtor.callAsConstructor(runtime, std::move(executorFunc));
|
|
125
125
|
}
|
|
126
126
|
bool CommonGlobals::Promise::isInstanceOf(jsi::Runtime& runtime, const jsi::Object& object) {
|
package/cpp/utils/JSCallback.hpp
CHANGED
|
@@ -35,7 +35,7 @@ public:
|
|
|
35
35
|
public:
|
|
36
36
|
/**
|
|
37
37
|
* Calls this `SyncJSCallback` synchronously, and
|
|
38
|
-
* returns
|
|
38
|
+
* returns its result (`R`).
|
|
39
39
|
* The callee is responsible for ensuring that the
|
|
40
40
|
* underlying `jsi::Function` can actually be called from this Thread.
|
|
41
41
|
* In Debug, sanity checks are made to ensure the `jsi::Function` is still alive.
|
|
@@ -14,7 +14,7 @@ using namespace facebook;
|
|
|
14
14
|
|
|
15
15
|
std::unordered_map<jsi::Runtime*, PropNameIDCache::CacheMap> PropNameIDCache::_cache;
|
|
16
16
|
|
|
17
|
-
const jsi::PropNameID& PropNameIDCache::get(jsi::Runtime& runtime, std::string value) {
|
|
17
|
+
const jsi::PropNameID& PropNameIDCache::get(jsi::Runtime& runtime, const std::string& value) {
|
|
18
18
|
CacheMap& cache = _cache[&runtime];
|
|
19
19
|
const auto& cachedName = cache.find(value);
|
|
20
20
|
if (cachedName != cache.end()) {
|
|
@@ -31,10 +31,11 @@ public:
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Get a `jsi::PropNameID` for the given `std::string` value.
|
|
34
|
-
* The `
|
|
35
|
-
*
|
|
34
|
+
* - The `std::string` must be an ASCII string.
|
|
35
|
+
* - The `jsi::PropNameID` is only valid within the callee's current
|
|
36
|
+
* synchronous scope, and must be non-escaping.
|
|
36
37
|
*/
|
|
37
|
-
static const jsi::PropNameID& get(jsi::Runtime& runtime, std::string value);
|
|
38
|
+
static const jsi::PropNameID& get(jsi::Runtime& runtime, const std::string& value);
|
|
38
39
|
|
|
39
40
|
private:
|
|
40
41
|
using CacheMap = std::unordered_map<std::string, BorrowingReference<jsi::PropNameID>>;
|
|
@@ -30,7 +30,7 @@ public protocol HybridObject: AnyObject {
|
|
|
30
30
|
* Eagerly- (and manually-) dispose all native resources this `HybridObject` holds.
|
|
31
31
|
* This method can only be manually called from JS using `dispose()`.
|
|
32
32
|
*
|
|
33
|
-
* If this method is never manually called, a `HybridObject` is expected to disposes
|
|
33
|
+
* If this method is never manually called, a `HybridObject` is expected to disposes its
|
|
34
34
|
* resources as usual via the object's destructor (`~HybridObject()`, `deinit` or `finalize()`).
|
|
35
35
|
*
|
|
36
36
|
* By default, this method does nothing. It can be overridden to perform actual disposing/cleanup
|
|
@@ -31,7 +31,7 @@ extension SwiftClosure {
|
|
|
31
31
|
* This can then be called from both C++ and Swift.
|
|
32
32
|
*/
|
|
33
33
|
public init(wrappingClosure closure: @escaping () -> Void) {
|
|
34
|
-
// Wrap closure in void*, and increment
|
|
34
|
+
// Wrap closure in void*, and increment its ref count so it stays alive.
|
|
35
35
|
let context = Unmanaged.passRetained(ClosureWrapper(closure: closure)).toOpaque()
|
|
36
36
|
|
|
37
37
|
// Create a C-style Function Pointer, which calls the actual Swift closure.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-modules",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.1",
|
|
4
4
|
"description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|