react-native-nitro-modules 0.1.5 → 0.1.7
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/cpp/core/HybridObject.cpp +4 -4
- package/cpp/jsi/JSICache.cpp +56 -0
- package/cpp/jsi/JSICache.hpp +53 -90
- package/cpp/jsi/JSIConverter+AnyMap.hpp +22 -0
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +13 -7
- package/cpp/jsi/JSIConverter+Function.hpp +10 -3
- package/cpp/jsi/JSIConverter+HybridObject.hpp +16 -0
- package/cpp/jsi/JSIConverter+Optional.hpp +9 -0
- package/cpp/jsi/JSIConverter+Promise.hpp +4 -0
- package/cpp/jsi/JSIConverter+Tuple.hpp +33 -0
- package/cpp/jsi/JSIConverter+UnorderedMap.hpp +20 -0
- package/cpp/jsi/JSIConverter+Variant.hpp +25 -53
- package/cpp/jsi/JSIConverter+Vector.hpp +21 -0
- package/cpp/jsi/JSIConverter.hpp +55 -7
- package/cpp/jsi/JSIHelpers.hpp +52 -0
- package/cpp/jsi/Promise.cpp +7 -3
- package/cpp/threading/Dispatcher.cpp +1 -1
- package/cpp/turbomodule/NativeNitroModules.cpp +0 -1
- package/cpp/utils/OwningReference.hpp +22 -0
- package/ios/turbomodule/NitroModuleOnLoad.mm +0 -5
- package/lib/commonjs/index.js +0 -11
- package/lib/commonjs/index.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/typescript/AnyMap.d.ts +17 -0
- package/lib/typescript/AnyMap.d.ts.map +1 -0
- package/lib/typescript/HybridObject.d.ts +58 -0
- package/lib/typescript/HybridObject.d.ts.map +1 -0
- package/lib/typescript/ModuleNotFoundError.d.ts +7 -0
- package/lib/typescript/ModuleNotFoundError.d.ts.map +1 -0
- package/lib/typescript/NativeNitroModules.d.ts +13 -0
- package/lib/typescript/NativeNitroModules.d.ts.map +1 -0
- package/lib/typescript/NativeNitroModules.web.d.ts +5 -0
- package/lib/typescript/NativeNitroModules.web.d.ts.map +1 -0
- package/lib/typescript/NitroModules.d.ts +18 -0
- package/lib/typescript/NitroModules.d.ts.map +1 -0
- package/lib/typescript/__tests__/index.test.d.ts +1 -0
- package/lib/typescript/__tests__/index.test.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +0 -1
- package/cpp/test-object/TestHybridObject.cpp +0 -37
- package/cpp/test-object/TestHybridObject.hpp +0 -87
- package/cpp/utils/GetRuntimeID.hpp +0 -28
- package/lib/commonjs/createTestObject.js +0 -15
- package/lib/commonjs/createTestObject.js.map +0 -1
- package/lib/module/createTestObject.js +0 -8
- package/lib/module/createTestObject.js.map +0 -1
- package/src/createTestObject.ts +0 -40
|
@@ -99,7 +99,7 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// 2. There is either no value in cache, or it's already dead. Create a new one
|
|
102
|
-
auto cache = JSICache
|
|
102
|
+
auto cache = JSICache::getOrCreateCache(runtime);
|
|
103
103
|
// 3. Create a new jsi::Object from this HybridObject/jsi::HostObject
|
|
104
104
|
std::shared_ptr<HybridObject> shared = shared_from_this();
|
|
105
105
|
jsi::Object object = jsi::Object::createFromHostObject(runtime, shared);
|
|
@@ -107,7 +107,7 @@ jsi::Value HybridObject::toObject(jsi::Runtime& runtime) {
|
|
|
107
107
|
size_t memorySize = getTotalExternalMemorySize();
|
|
108
108
|
object.setExternalMemoryPressure(runtime, memorySize);
|
|
109
109
|
// 5. Make it a global (weak) reference
|
|
110
|
-
auto global = cache.makeGlobal(std::move(object));
|
|
110
|
+
auto global = cache.makeGlobal<jsi::Object>(std::move(object));
|
|
111
111
|
_jsObjects[&runtime] = global.weak();
|
|
112
112
|
// 6. Return a jsi::Value copy again to the caller.
|
|
113
113
|
return jsi::Value(runtime, *global);
|
|
@@ -177,12 +177,12 @@ jsi::Value HybridObject::get(facebook::jsi::Runtime& runtime, const facebook::js
|
|
|
177
177
|
// it's a function. we now need to wrap it in a jsi::Function, store it in cache, then return it.
|
|
178
178
|
HybridFunction& hybridFunction = _methods.at(name);
|
|
179
179
|
// get (or create) a runtime-specific function cache
|
|
180
|
-
auto runtimeCache = JSICache
|
|
180
|
+
auto runtimeCache = JSICache::getOrCreateCache(runtime);
|
|
181
181
|
// create the jsi::Function
|
|
182
182
|
jsi::Function function = jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, name),
|
|
183
183
|
hybridFunction.parameterCount, hybridFunction.function);
|
|
184
184
|
// throw it into the cache for next time
|
|
185
|
-
OwningReference<jsi::Function> globalFunction = runtimeCache.makeGlobal(std::move(function));
|
|
185
|
+
OwningReference<jsi::Function> globalFunction = runtimeCache.makeGlobal<jsi::Function>(std::move(function));
|
|
186
186
|
functionCache[name] = globalFunction;
|
|
187
187
|
// copy the reference & return it to JS
|
|
188
188
|
return jsi::Value(runtime, *globalFunction);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JSICache.cpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 20.06.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#include "JSICache.hpp"
|
|
9
|
+
#include "JSIHelpers.hpp"
|
|
10
|
+
|
|
11
|
+
namespace margelo::nitro {
|
|
12
|
+
|
|
13
|
+
static constexpr auto CACHE_PROP_NAME = "__nitroModulesJSICache";
|
|
14
|
+
|
|
15
|
+
JSICache::~JSICache() {
|
|
16
|
+
Logger::log(TAG, "Destroying JSICache...");
|
|
17
|
+
std::unique_lock lock(_mutex);
|
|
18
|
+
|
|
19
|
+
for (auto& func : _cache) {
|
|
20
|
+
OwningReference<jsi::Object> owning = func.lock();
|
|
21
|
+
if (owning) {
|
|
22
|
+
// Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
|
|
23
|
+
owning.destroy();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
JSICacheReference JSICache::getOrCreateCache(jsi::Runtime& runtime) {
|
|
29
|
+
auto found = _globalCache.find(&runtime);
|
|
30
|
+
if (found != _globalCache.end()) {
|
|
31
|
+
// Fast path: get weak_ptr to JSICache from our global list.
|
|
32
|
+
std::weak_ptr<JSICache> weak = found->second;
|
|
33
|
+
std::shared_ptr<JSICache> strong = weak.lock();
|
|
34
|
+
if (strong) {
|
|
35
|
+
// It's still alive! Return it
|
|
36
|
+
return JSICacheReference(strong);
|
|
37
|
+
}
|
|
38
|
+
Logger::log(TAG, "JSICache was created, but it is no longer strong!");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Cache doesn't exist yet.
|
|
42
|
+
Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
|
|
43
|
+
// Create new cache
|
|
44
|
+
auto nativeState = std::make_shared<JSICache>(&runtime);
|
|
45
|
+
// Wrap it in a jsi::Value using NativeState
|
|
46
|
+
jsi::Object cache(runtime);
|
|
47
|
+
cache.setNativeState(runtime, nativeState);
|
|
48
|
+
// Inject it into the jsi::Runtime's global so it's memory is managed by it
|
|
49
|
+
runtime.global().setProperty(runtime, CACHE_PROP_NAME, std::move(cache));
|
|
50
|
+
// Add it to our map of caches
|
|
51
|
+
_globalCache[&runtime] = nativeState;
|
|
52
|
+
// Return it
|
|
53
|
+
return JSICacheReference(nativeState);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
} // namespace margelo::nitro
|
package/cpp/jsi/JSICache.hpp
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
10
|
#include "BorrowingReference.hpp"
|
|
11
|
-
#include "GetRuntimeID.hpp"
|
|
12
11
|
#include "NitroLogger.hpp"
|
|
13
12
|
#include "OwningReference.hpp"
|
|
14
13
|
#include <jsi/jsi.h>
|
|
@@ -21,73 +20,23 @@ namespace margelo::nitro {
|
|
|
21
20
|
|
|
22
21
|
using namespace facebook;
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
template <typename T>
|
|
27
|
-
class JSICache;
|
|
28
|
-
|
|
29
|
-
template <typename T>
|
|
30
|
-
class JSICacheReference final {
|
|
31
|
-
public:
|
|
32
|
-
JSICacheReference() = delete;
|
|
33
|
-
JSICacheReference(const JSICacheReference&) = delete;
|
|
34
|
-
JSICacheReference(JSICacheReference&&) = delete;
|
|
35
|
-
|
|
36
|
-
~JSICacheReference() {
|
|
37
|
-
_strongCache->_mutex.unlock();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
public:
|
|
41
|
-
/**
|
|
42
|
-
Creates a reference to a `jsi::Value` that can be stored in memory and accessed later.
|
|
43
|
-
The `jsi::Value` will be managed by the `jsi::Runtime`, if the `jsi::Runtime` gets the destroyed,
|
|
44
|
-
so will the `jsi::Value`.
|
|
45
|
-
|
|
46
|
-
To access the `jsi::Value`, try to `.lock()` the `weak_ptr`.
|
|
47
|
-
If it can be locked, it is still valid, otherwise the Runtime has already been deleted.
|
|
48
|
-
Do not hold the returned `shared_ptr` in memory, only use it in the calling function's scope.
|
|
49
|
-
Note: By design, this is not thread-safe, the returned `weak_ptr` must only be locked on the same thread as it was created on.
|
|
50
|
-
*/
|
|
51
|
-
OwningReference<T> makeGlobal(T&& value) {
|
|
52
|
-
auto owning = OwningReference<T>(new T(std::move(value)));
|
|
53
|
-
_strongCache->_cache.push_back(owning.weak());
|
|
54
|
-
return owning;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private:
|
|
58
|
-
explicit JSICacheReference(const std::shared_ptr<JSICache<T>>& cache) : _strongCache(cache) {
|
|
59
|
-
_strongCache->_mutex.lock();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private:
|
|
63
|
-
std::shared_ptr<JSICache<T>> _strongCache;
|
|
64
|
-
|
|
65
|
-
friend class JSICache<T>;
|
|
66
|
-
};
|
|
23
|
+
class JSICacheReference;
|
|
67
24
|
|
|
68
25
|
/**
|
|
69
|
-
* A `JSICache` can safely store `jsi::
|
|
26
|
+
* A `JSICache` can safely store `jsi::Object` instances (e.g. `jsi::Object` or
|
|
70
27
|
* `jsi::Function`) inside `OwningReference<T>`.
|
|
71
28
|
*
|
|
72
|
-
* `jsi::
|
|
73
|
-
* is deleted - even if there are still strong references to the `jsi::
|
|
29
|
+
* `jsi::Object`s are managed by a `jsi::Runtime`, and will be deleted if the `jsi::Runtime`
|
|
30
|
+
* is deleted - even if there are still strong references to the `jsi::Object`.
|
|
31
|
+
*
|
|
32
|
+
* To access a `OwningReference<jsi::Object>` safely, use `lock()` to get an `OwningLock<jsi::Object>`.
|
|
33
|
+
* This will allow you to access the `jsi::Object` as long as the `OwningLock` is alive,
|
|
34
|
+
* and `JSICache` will hold any garbage collection calls until the `OwningLock` is destroyed.
|
|
74
35
|
*/
|
|
75
|
-
template <typename T = jsi::Pointer>
|
|
76
36
|
class JSICache final : public jsi::NativeState {
|
|
77
37
|
public:
|
|
78
38
|
explicit JSICache(jsi::Runtime* runtime) : _runtime(runtime) {}
|
|
79
|
-
|
|
80
|
-
~JSICache() {
|
|
81
|
-
std::unique_lock lock(_mutex);
|
|
82
|
-
|
|
83
|
-
for (auto& func : _cache) {
|
|
84
|
-
OwningReference<T> owning = func.lock();
|
|
85
|
-
if (owning) {
|
|
86
|
-
// Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
|
|
87
|
-
owning.destroy();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
39
|
+
~JSICache();
|
|
91
40
|
|
|
92
41
|
public:
|
|
93
42
|
JSICache() = delete;
|
|
@@ -102,47 +51,61 @@ public:
|
|
|
102
51
|
Do not hold the returned `shared_ptr` in memory, only use it in the calling function's scope.
|
|
103
52
|
*/
|
|
104
53
|
[[nodiscard]]
|
|
105
|
-
static JSICacheReference
|
|
106
|
-
auto found = _globalCache.find(&runtime);
|
|
107
|
-
if (found != _globalCache.end()) {
|
|
108
|
-
// Fast path: get weak_ptr to JSICache from our global list.
|
|
109
|
-
std::weak_ptr<JSICache<T>> weak = found->second;
|
|
110
|
-
std::shared_ptr<JSICache<T>> strong = weak.lock();
|
|
111
|
-
if (strong) {
|
|
112
|
-
// It's still alive! Return it
|
|
113
|
-
return JSICacheReference<T>(strong);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Cache doesn't exist yet.
|
|
118
|
-
Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
|
|
119
|
-
// Create new cache
|
|
120
|
-
auto nativeState = std::make_shared<JSICache<T>>(&runtime);
|
|
121
|
-
// Wrap it in a jsi::Value using NativeState
|
|
122
|
-
jsi::Object cache(runtime);
|
|
123
|
-
cache.setNativeState(runtime, nativeState);
|
|
124
|
-
// Inject it into the jsi::Runtime's global so it's memory is managed by it
|
|
125
|
-
runtime.global().setProperty(runtime, CACHE_PROP_NAME, std::move(cache));
|
|
126
|
-
// Add it to our map of caches
|
|
127
|
-
_globalCache[&runtime] = nativeState;
|
|
128
|
-
// Return it
|
|
129
|
-
return JSICacheReference<T>(nativeState);
|
|
130
|
-
}
|
|
54
|
+
static JSICacheReference getOrCreateCache(jsi::Runtime& runtime);
|
|
131
55
|
|
|
132
|
-
public:
|
|
133
56
|
private:
|
|
134
|
-
friend class JSICacheReference
|
|
57
|
+
friend class JSICacheReference;
|
|
135
58
|
|
|
136
59
|
private:
|
|
137
60
|
jsi::Runtime* _runtime;
|
|
138
61
|
std::mutex _mutex;
|
|
139
|
-
std::vector<BorrowingReference<
|
|
62
|
+
std::vector<BorrowingReference<jsi::Object>> _cache;
|
|
140
63
|
|
|
141
64
|
private:
|
|
142
|
-
static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache
|
|
65
|
+
static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache>> _globalCache;
|
|
143
66
|
|
|
144
67
|
private:
|
|
145
68
|
static constexpr auto TAG = "JSICache";
|
|
146
69
|
};
|
|
147
70
|
|
|
71
|
+
class JSICacheReference final {
|
|
72
|
+
public:
|
|
73
|
+
JSICacheReference() = delete;
|
|
74
|
+
JSICacheReference(const JSICacheReference&) = delete;
|
|
75
|
+
JSICacheReference(JSICacheReference&&) = delete;
|
|
76
|
+
|
|
77
|
+
~JSICacheReference() {
|
|
78
|
+
_strongCache->_mutex.unlock();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public:
|
|
82
|
+
/**
|
|
83
|
+
* Creates a semi-strong reference to a `jsi::Object` that can be stored in memory and accessed later.
|
|
84
|
+
*
|
|
85
|
+
* The returned `OwningReference<T>` keeps the `T` semi-strong, and once it gets deleted, `T` will also be freed.
|
|
86
|
+
*
|
|
87
|
+
* Note: The JS Runtime can still delete `T` at any point (e.g. when it gets destroyed or reloaded),
|
|
88
|
+
* so any operations on `T` should be under an `OwningLock<T>`.
|
|
89
|
+
*
|
|
90
|
+
* To get an `OwningLock<T>` (which implies safe access to `T`), call `.locK()` on the `OwningReference<T>`.
|
|
91
|
+
* This will hold off any JS Runtime GC calls that will destroy `T` for as long as the `OwningLock<T>` is alive.
|
|
92
|
+
*/
|
|
93
|
+
template <typename T>
|
|
94
|
+
OwningReference<T> makeGlobal(T&& value) {
|
|
95
|
+
OwningReference<jsi::Object> owning(new T(std::move(value)));
|
|
96
|
+
_strongCache->_cache.push_back(owning.weak());
|
|
97
|
+
return owning.as<T>();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private:
|
|
101
|
+
explicit JSICacheReference(const std::shared_ptr<JSICache>& cache) : _strongCache(cache) {
|
|
102
|
+
_strongCache->_mutex.lock();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private:
|
|
106
|
+
std::shared_ptr<JSICache> _strongCache;
|
|
107
|
+
|
|
108
|
+
friend class JSICache;
|
|
109
|
+
};
|
|
110
|
+
|
|
148
111
|
} // namespace margelo::nitro
|
|
@@ -17,6 +17,7 @@ struct JSIConverter;
|
|
|
17
17
|
#include "JSIConverter.hpp"
|
|
18
18
|
|
|
19
19
|
#include "AnyMap.hpp"
|
|
20
|
+
#include "JSIHelpers.hpp"
|
|
20
21
|
#include <jsi/jsi.h>
|
|
21
22
|
#include <memory>
|
|
22
23
|
|
|
@@ -33,6 +34,9 @@ struct JSIConverter<AnyValue> {
|
|
|
33
34
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const AnyValue& value) {
|
|
34
35
|
return JSIConverter<AnyValue::variant>::toJSI(runtime, value);
|
|
35
36
|
}
|
|
37
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
38
|
+
return JSIConverter<AnyValue::variant>::canConvert(runtime, value);
|
|
39
|
+
}
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
// AnyMap <> Record<K, V>
|
|
@@ -59,6 +63,24 @@ struct JSIConverter<std::shared_ptr<AnyMap>> {
|
|
|
59
63
|
}
|
|
60
64
|
return object;
|
|
61
65
|
}
|
|
66
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
67
|
+
if (!value.isObject()) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
jsi::Object object = value.getObject(runtime);
|
|
71
|
+
if (!isPlainObject(runtime, object)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
jsi::Array properties = object.getPropertyNames(runtime);
|
|
75
|
+
size_t size = properties.size(runtime);
|
|
76
|
+
for (size_t i = 0; i < size; i++) {
|
|
77
|
+
bool canConvertProp = JSIConverter<AnyValue>::canConvert(runtime, properties.getValueAtIndex(runtime, i));
|
|
78
|
+
if (!canConvertProp) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
62
84
|
};
|
|
63
85
|
|
|
64
86
|
} // namespace margelo::nitro
|
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
9
|
class ArrayBuffer;
|
|
10
|
-
|
|
11
|
-
template <typename T>
|
|
12
|
-
struct JSICache;
|
|
10
|
+
class JSICache;
|
|
13
11
|
|
|
14
12
|
template <typename T, typename Enable>
|
|
15
13
|
struct JSIConverter;
|
|
@@ -34,17 +32,25 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer
|
|
|
34
32
|
static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
35
33
|
jsi::Object object = arg.asObject(runtime);
|
|
36
34
|
if (!object.isArrayBuffer(runtime)) [[unlikely]] {
|
|
37
|
-
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) + "\" is not an ArrayBuffer!"
|
|
35
|
+
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) + "\" is not an ArrayBuffer! "
|
|
36
|
+
"Are you maybe passing a TypedArray (e.g. Uint8Array)? Try to pass it's `.buffer` value.");
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
JSICacheReference
|
|
41
|
-
auto owningArrayBuffer = cache.makeGlobal(object.getArrayBuffer(runtime));
|
|
39
|
+
JSICacheReference cache = JSICache::getOrCreateCache(runtime);
|
|
40
|
+
auto owningArrayBuffer = cache.makeGlobal<jsi::ArrayBuffer>(object.getArrayBuffer(runtime));
|
|
42
41
|
|
|
43
42
|
return std::make_shared<JSArrayBuffer>(&runtime, owningArrayBuffer);
|
|
44
43
|
}
|
|
45
|
-
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<jsi::MutableBuffer
|
|
44
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<jsi::MutableBuffer>& buffer) {
|
|
46
45
|
return jsi::ArrayBuffer(runtime, buffer);
|
|
47
46
|
}
|
|
47
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
48
|
+
if (value.isObject()) {
|
|
49
|
+
jsi::Object object = value.getObject(runtime);
|
|
50
|
+
return object.isArrayBuffer(runtime);
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
48
54
|
};
|
|
49
55
|
|
|
50
56
|
} // namespace margelo::nitro
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
|
-
template <typename T>
|
|
10
9
|
class JSICache;
|
|
11
10
|
|
|
12
11
|
template <typename T, typename Enable>
|
|
@@ -34,9 +33,9 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
34
33
|
|
|
35
34
|
static inline std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
36
35
|
// Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
|
|
37
|
-
auto cache = JSICache
|
|
36
|
+
auto cache = JSICache::getOrCreateCache(runtime);
|
|
38
37
|
jsi::Function function = arg.asObject(runtime).asFunction(runtime);
|
|
39
|
-
OwningReference<jsi::Function> sharedFunction = cache.makeGlobal(std::move(function));
|
|
38
|
+
OwningReference<jsi::Function> sharedFunction = cache.makeGlobal<jsi::Function>(std::move(function));
|
|
40
39
|
|
|
41
40
|
std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
42
41
|
std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
|
|
@@ -80,6 +79,14 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
80
79
|
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "hostFunction"), sizeof...(Args), jsFunction);
|
|
81
80
|
}
|
|
82
81
|
|
|
82
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
83
|
+
if (value.isObject()) {
|
|
84
|
+
jsi::Object object = value.getObject(runtime);
|
|
85
|
+
return object.isFunction(runtime);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
83
90
|
private:
|
|
84
91
|
static inline ResultingType callJSFunction(jsi::Runtime& runtime, const OwningReference<jsi::Function>& function, const Args&... args) {
|
|
85
92
|
// Throw a lock on the OwningReference<T> so we can guarantee safe access (Hermes GC cannot delete it while `lock` is alive)
|
|
@@ -72,6 +72,14 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::HostObject>>>
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
76
|
+
if (value.isObject()) {
|
|
77
|
+
jsi::Object object = value.getObject(runtime);
|
|
78
|
+
return object.isHostObject<TPointee>(runtime);
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
private:
|
|
76
84
|
static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
|
|
77
85
|
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
@@ -120,6 +128,14 @@ struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>
|
|
|
120
128
|
return object;
|
|
121
129
|
}
|
|
122
130
|
|
|
131
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
132
|
+
if (value.isObject()) {
|
|
133
|
+
jsi::Object object = value.getObject(runtime);
|
|
134
|
+
return object.hasNativeState<TPointee>(runtime);
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
123
139
|
private:
|
|
124
140
|
static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
|
|
125
141
|
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
@@ -36,6 +36,15 @@ struct JSIConverter<std::optional<TInner>> {
|
|
|
36
36
|
return JSIConverter<TInner>::toJSI(runtime, arg.value());
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
40
|
+
if (value.isUndefined() || value.isNull()) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (JSIConverter<TInner>::canConvert(runtime, value)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
39
48
|
};
|
|
40
49
|
|
|
41
50
|
} // namespace margelo::nitro
|
|
@@ -82,6 +82,10 @@ struct JSIConverter<std::future<TResult>> {
|
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
|
+
|
|
86
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
87
|
+
throw std::runtime_error("jsi::Value of type Promise cannot be converted to std::future yet!");
|
|
88
|
+
}
|
|
85
89
|
};
|
|
86
90
|
|
|
87
91
|
} // namespace margelo::nitro
|
|
@@ -42,6 +42,23 @@ struct JSIConverter<std::tuple<Types...>> {
|
|
|
42
42
|
return array;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
46
|
+
if (!value.isObject()) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
jsi::Object object = value.getObject(runtime);
|
|
50
|
+
if (!object.isArray(runtime)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
jsi::Array array = object.getArray(runtime);
|
|
54
|
+
size_t size = array.size(runtime);
|
|
55
|
+
if (size != sizeof...(Types)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return canConvertRecursive<Types...>(runtime, array, 0);
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
private:
|
|
46
63
|
template <std::size_t... Is>
|
|
47
64
|
static inline std::tuple<Types...> copyArrayItemsToTuple(jsi::Runtime& runtime, const jsi::Array& array, std::index_sequence<Is...>) {
|
|
@@ -55,6 +72,22 @@ private:
|
|
|
55
72
|
JSIConverter<std::tuple_element_t<Is, std::tuple<Types...>>>::toJSI(runtime, std::get<Is>(tuple)))),
|
|
56
73
|
...);
|
|
57
74
|
}
|
|
75
|
+
|
|
76
|
+
template <typename T>
|
|
77
|
+
static bool canConvertElement(jsi::Runtime& runtime, const jsi::Array& array, size_t index) {
|
|
78
|
+
return JSIConverter<T>::canConvert(runtime, array.getValueAtIndex(runtime, index));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
template <typename First, typename... Rest>
|
|
82
|
+
static bool canConvertRecursive(jsi::Runtime& runtime, const jsi::Array& array, size_t index) {
|
|
83
|
+
if (!canConvertElement<First>(runtime, array, index)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if constexpr (sizeof...(Rest) > 0) {
|
|
87
|
+
return canConvertRecursive<Rest...>(runtime, array, index + 1);
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
58
91
|
};
|
|
59
92
|
|
|
60
93
|
} // namespace margelo::nitro
|
|
@@ -13,6 +13,7 @@ struct JSIConverter;
|
|
|
13
13
|
#include "JSIConverter.hpp"
|
|
14
14
|
|
|
15
15
|
#include "AnyMap.hpp"
|
|
16
|
+
#include "JSIHelpers.hpp"
|
|
16
17
|
#include <jsi/jsi.h>
|
|
17
18
|
#include <unordered_map>
|
|
18
19
|
|
|
@@ -45,6 +46,25 @@ struct JSIConverter<std::unordered_map<std::string, ValueType>> {
|
|
|
45
46
|
}
|
|
46
47
|
return object;
|
|
47
48
|
}
|
|
49
|
+
|
|
50
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
51
|
+
if (!value.isObject()) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
jsi::Object object = value.getObject(runtime);
|
|
55
|
+
if (!isPlainObject(runtime, object)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
jsi::Array properties = object.getPropertyNames(runtime);
|
|
59
|
+
size_t size = properties.size(runtime);
|
|
60
|
+
for (size_t i = 0; i < size; i++) {
|
|
61
|
+
bool canConvertProp = JSIConverter<ValueType>::canConvert(runtime, properties.getValueAtIndex(runtime, i));
|
|
62
|
+
if (!canConvertProp) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
48
68
|
};
|
|
49
69
|
|
|
50
70
|
} // namespace margelo::nitro
|
|
@@ -31,66 +31,38 @@ using namespace facebook;
|
|
|
31
31
|
// std::variant<A, B, C> <> A | B | C
|
|
32
32
|
template <typename... Types>
|
|
33
33
|
struct JSIConverter<std::variant<Types...>> {
|
|
34
|
+
|
|
35
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
36
|
+
// Check each type in `Types...` to make sure we can convert `jsi::Value` to one of those.
|
|
37
|
+
return (JSIConverter<Types>::canConvert(runtime, value) || ...);
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
static inline std::variant<Types...> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
35
|
-
|
|
36
|
-
if constexpr (is_in_pack_v<std::monostate, Types...>) {
|
|
37
|
-
return std::monostate();
|
|
38
|
-
} else {
|
|
39
|
-
throw typeNotSupportedError("null");
|
|
40
|
-
}
|
|
41
|
-
} else if (value.isBool()) {
|
|
42
|
-
if constexpr (is_in_pack_v<bool, Types...>) {
|
|
43
|
-
return JSIConverter<bool>::fromJSI(runtime, value);
|
|
44
|
-
} else {
|
|
45
|
-
throw typeNotSupportedError("boolean");
|
|
46
|
-
}
|
|
47
|
-
} else if (value.isNumber()) {
|
|
48
|
-
if constexpr (is_in_pack_v<double, Types...>) {
|
|
49
|
-
return JSIConverter<double>::fromJSI(runtime, value);
|
|
50
|
-
} else {
|
|
51
|
-
throw typeNotSupportedError("number");
|
|
52
|
-
}
|
|
53
|
-
} else if (value.isString()) {
|
|
54
|
-
if constexpr (is_in_pack_v<std::string, Types...>) {
|
|
55
|
-
return JSIConverter<std::string>::fromJSI(runtime, value);
|
|
56
|
-
} else {
|
|
57
|
-
throw typeNotSupportedError("string");
|
|
58
|
-
}
|
|
59
|
-
} else if (value.isBigInt()) {
|
|
60
|
-
if constexpr (is_in_pack_v<int64_t, Types...>) {
|
|
61
|
-
return JSIConverter<int64_t>::fromJSI(runtime, value);
|
|
62
|
-
} else {
|
|
63
|
-
throw typeNotSupportedError("bigint");
|
|
64
|
-
}
|
|
65
|
-
} else if (value.isObject()) {
|
|
66
|
-
jsi::Object valueObj = value.getObject(runtime);
|
|
67
|
-
if (valueObj.isArray(runtime)) {
|
|
68
|
-
if constexpr (is_in_pack_v<std::vector<AnyValue>, Types...>) {
|
|
69
|
-
return JSIConverter<std::vector<AnyValue>>::fromJSI(runtime, value);
|
|
70
|
-
} else {
|
|
71
|
-
throw typeNotSupportedError("array[]");
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
if constexpr (is_in_pack_v<std::unordered_map<std::string, AnyValue>, Types...>) {
|
|
75
|
-
return JSIConverter<std::unordered_map<std::string, AnyValue>>::fromJSI(runtime, value);
|
|
76
|
-
} else {
|
|
77
|
-
throw typeNotSupportedError("object{}");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} else {
|
|
81
|
-
std::string stringRepresentation = value.toString(runtime).utf8(runtime);
|
|
82
|
-
throw std::runtime_error("Cannot convert \"" + stringRepresentation + "\" to std::variant<...>!");
|
|
83
|
-
}
|
|
41
|
+
return fromJSIRecursive<Types...>(runtime, value);
|
|
84
42
|
}
|
|
85
43
|
|
|
86
44
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::variant<Types...>& variant) {
|
|
87
|
-
return std::visit(
|
|
45
|
+
return std::visit(
|
|
46
|
+
[&runtime](const auto& val) {
|
|
47
|
+
// Try to convert each type
|
|
48
|
+
return JSIConverter<std::decay_t<decltype(val)>>::toJSI(runtime, val);
|
|
49
|
+
},
|
|
50
|
+
variant);
|
|
88
51
|
}
|
|
89
52
|
|
|
90
53
|
private:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
54
|
+
template <typename First, typename... Rest>
|
|
55
|
+
static inline std::variant<Types...> fromJSIRecursive(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
56
|
+
if (JSIConverter<First>::canConvert(runtime, value)) {
|
|
57
|
+
return JSIConverter<First>::fromJSI(runtime, value);
|
|
58
|
+
}
|
|
59
|
+
if constexpr (sizeof...(Rest) == 0) {
|
|
60
|
+
std::string string = value.toString(runtime).utf8(runtime);
|
|
61
|
+
std::string types = TypeInfo::getFriendlyTypenames<Types...>();
|
|
62
|
+
throw std::runtime_error("Cannot convert \"" + string + "\" to any type in variant<" + types + ">!");
|
|
63
|
+
} else {
|
|
64
|
+
return fromJSIRecursive<Rest...>(runtime, value);
|
|
65
|
+
}
|
|
94
66
|
}
|
|
95
67
|
};
|
|
96
68
|
|
|
@@ -43,6 +43,27 @@ struct JSIConverter<std::vector<ElementType>> {
|
|
|
43
43
|
}
|
|
44
44
|
return array;
|
|
45
45
|
}
|
|
46
|
+
|
|
47
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
48
|
+
if (!value.isObject()) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
jsi::Object object = value.getObject(runtime);
|
|
52
|
+
if (!object.isArray(runtime)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
jsi::Array array = object.getArray(runtime);
|
|
56
|
+
if (array.size(runtime) == 0) {
|
|
57
|
+
// it is an empty array, so it _theoretically_ doesn't matter what type it holds. Just say true.
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// Check the type of the first element in the array.
|
|
61
|
+
// Technically the array can also have different types for each item,
|
|
62
|
+
// and to be absolutely sure that we can convert the entire array, we have to check each item in the array.
|
|
63
|
+
// But we don't want to do that for performance reasons - let's just assume the user doesn't make this mistake.
|
|
64
|
+
jsi::Value firstElement = array.getValueAtIndex(runtime, 0);
|
|
65
|
+
return JSIConverter<ElementType>::canConvert(runtime, firstElement);
|
|
66
|
+
}
|
|
46
67
|
};
|
|
47
68
|
|
|
48
69
|
} // namespace margelo::nitro
|