react-native-nitro-modules 0.1.6 → 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.
- package/README.md +7 -3
- package/android/CMakeLists.txt +1 -0
- package/cpp/core/HybridFunction.hpp +167 -0
- package/cpp/core/HybridObject.cpp +37 -128
- package/cpp/core/HybridObject.hpp +13 -149
- package/cpp/jsi/JSICache.cpp +25 -8
- package/cpp/jsi/JSICache.hpp +31 -24
- package/cpp/jsi/JSIConverter+AnyMap.hpp +22 -0
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +15 -3
- package/cpp/jsi/JSIConverter+Function.hpp +9 -1
- package/cpp/jsi/JSIConverter+HybridObject.hpp +31 -76
- 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 +2 -2
- package/cpp/prototype/HybridObjectPrototype.cpp +87 -0
- package/cpp/prototype/HybridObjectPrototype.hpp +92 -0
- package/cpp/prototype/Prototype.hpp +163 -0
- package/cpp/prototype/PrototypeChain.hpp +78 -0
- package/cpp/threading/Dispatcher.cpp +1 -1
- package/cpp/turbomodule/NativeNitroModules.cpp +3 -4
- package/cpp/turbomodule/NativeNitroModules.hpp +1 -1
- package/cpp/utils/BorrowingReference+Owning.hpp +1 -1
- package/cpp/utils/BorrowingReference.hpp +1 -1
- package/ios/turbomodule/NitroModuleOnLoad.mm +0 -5
- package/lib/HybridObject.d.ts +20 -1
- 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/HybridObject.d.ts +77 -0
- package/lib/typescript/HybridObject.d.ts.map +1 -0
- package/lib/typescript/NitroModules.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/HybridObject.ts +20 -1
- package/src/index.ts +0 -1
- package/cpp/core/PointerHolder.hpp +0 -94
- 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
package/cpp/jsi/JSICache.cpp
CHANGED
|
@@ -6,17 +6,18 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#include "JSICache.hpp"
|
|
9
|
+
#include "JSIHelpers.hpp"
|
|
10
|
+
|
|
11
|
+
#define DOUBLE_CHECK_GLOBAL_CACHE 1
|
|
9
12
|
|
|
10
13
|
namespace margelo::nitro {
|
|
11
14
|
|
|
12
15
|
static constexpr auto CACHE_PROP_NAME = "__nitroModulesJSICache";
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
for (auto& func : _cache) {
|
|
19
|
-
OwningReference<jsi::Object> owning = func.lock();
|
|
17
|
+
template <typename T>
|
|
18
|
+
inline void destroyReferences(const std::vector<BorrowingReference<T>>& references) {
|
|
19
|
+
for (auto& func : references) {
|
|
20
|
+
OwningReference<T> owning = func.lock();
|
|
20
21
|
if (owning) {
|
|
21
22
|
// Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
|
|
22
23
|
owning.destroy();
|
|
@@ -24,9 +25,19 @@ JSICache::~JSICache() {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
JSICache::~JSICache() {
|
|
29
|
+
Logger::log(TAG, "Destroying JSICache...");
|
|
30
|
+
std::unique_lock lock(_mutex);
|
|
31
|
+
|
|
32
|
+
destroyReferences(_objectCache);
|
|
33
|
+
destroyReferences(_functionCache);
|
|
34
|
+
destroyReferences(_weakObjectCache);
|
|
35
|
+
destroyReferences(_arrayBufferCache);
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
JSICacheReference JSICache::getOrCreateCache(jsi::Runtime& runtime) {
|
|
28
39
|
auto found = _globalCache.find(&runtime);
|
|
29
|
-
if (found != _globalCache.end()) {
|
|
40
|
+
if (found != _globalCache.end()) [[likely]] {
|
|
30
41
|
// Fast path: get weak_ptr to JSICache from our global list.
|
|
31
42
|
std::weak_ptr<JSICache> weak = found->second;
|
|
32
43
|
std::shared_ptr<JSICache> strong = weak.lock();
|
|
@@ -37,10 +48,16 @@ JSICacheReference JSICache::getOrCreateCache(jsi::Runtime& runtime) {
|
|
|
37
48
|
Logger::log(TAG, "JSICache was created, but it is no longer strong!");
|
|
38
49
|
}
|
|
39
50
|
|
|
51
|
+
#if DOUBLE_CHECK_GLOBAL_CACHE
|
|
52
|
+
if (runtime.global().hasProperty(runtime, CACHE_PROP_NAME)) [[unlikely]] {
|
|
53
|
+
throw std::runtime_error("The Runtime \"" + getRuntimeId(runtime) + "\" already has a global cache! (\"" + CACHE_PROP_NAME + "\")");
|
|
54
|
+
}
|
|
55
|
+
#endif
|
|
56
|
+
|
|
40
57
|
// Cache doesn't exist yet.
|
|
41
58
|
Logger::log(TAG, "Creating new JSICache<T> for runtime %s..", getRuntimeId(runtime));
|
|
42
59
|
// Create new cache
|
|
43
|
-
auto nativeState = std::
|
|
60
|
+
auto nativeState = std::shared_ptr<JSICache>(new JSICache(&runtime));
|
|
44
61
|
// Wrap it in a jsi::Value using NativeState
|
|
45
62
|
jsi::Object cache(runtime);
|
|
46
63
|
cache.setNativeState(runtime, nativeState);
|
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>
|
|
@@ -24,19 +23,18 @@ using namespace facebook;
|
|
|
24
23
|
class JSICacheReference;
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
|
-
* A `JSICache` can safely store `jsi::
|
|
26
|
+
* A `JSICache` can safely store `jsi::Value` instances (e.g. `jsi::Object` or
|
|
28
27
|
* `jsi::Function`) inside `OwningReference<T>`.
|
|
29
28
|
*
|
|
30
|
-
* `jsi::
|
|
31
|
-
* is deleted - even if there are still strong references to the `jsi::
|
|
29
|
+
* `jsi::Value`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::Value`.
|
|
32
31
|
*
|
|
33
|
-
* To access a `OwningReference<jsi::
|
|
34
|
-
* This will allow you to access the `jsi::
|
|
32
|
+
* To access a `OwningReference<jsi::Value>` safely, use `lock()` to get an `OwningLock<jsi::Value>`.
|
|
33
|
+
* This will allow you to access the `jsi::Value` as long as the `OwningLock` is alive,
|
|
35
34
|
* and `JSICache` will hold any garbage collection calls until the `OwningLock` is destroyed.
|
|
36
35
|
*/
|
|
37
36
|
class JSICache final : public jsi::NativeState {
|
|
38
37
|
public:
|
|
39
|
-
explicit JSICache(jsi::Runtime* runtime) : _runtime(runtime) {}
|
|
40
38
|
~JSICache();
|
|
41
39
|
|
|
42
40
|
public:
|
|
@@ -44,6 +42,9 @@ public:
|
|
|
44
42
|
JSICache(const JSICache&) = delete;
|
|
45
43
|
JSICache(JSICache&&) = delete;
|
|
46
44
|
|
|
45
|
+
private:
|
|
46
|
+
explicit JSICache(jsi::Runtime* runtime) : _runtime(runtime) {}
|
|
47
|
+
|
|
47
48
|
public:
|
|
48
49
|
/**
|
|
49
50
|
Gets or creates a `JSICache` for the given `jsi::Runtime`.
|
|
@@ -60,7 +61,10 @@ private:
|
|
|
60
61
|
private:
|
|
61
62
|
jsi::Runtime* _runtime;
|
|
62
63
|
std::mutex _mutex;
|
|
63
|
-
std::vector<BorrowingReference<jsi::Object>>
|
|
64
|
+
std::vector<BorrowingReference<jsi::Object>> _objectCache;
|
|
65
|
+
std::vector<BorrowingReference<jsi::Function>> _functionCache;
|
|
66
|
+
std::vector<BorrowingReference<jsi::WeakObject>> _weakObjectCache;
|
|
67
|
+
std::vector<BorrowingReference<jsi::ArrayBuffer>> _arrayBufferCache;
|
|
64
68
|
|
|
65
69
|
private:
|
|
66
70
|
static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache>> _globalCache;
|
|
@@ -80,22 +84,25 @@ public:
|
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
public:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
OwningReference<jsi::Object> makeShared(jsi::Object&& value) {
|
|
88
|
+
OwningReference<jsi::Object> owning(new jsi::Object(std::move(value)));
|
|
89
|
+
_strongCache->_objectCache.push_back(owning.weak());
|
|
90
|
+
return owning;
|
|
91
|
+
}
|
|
92
|
+
OwningReference<jsi::Function> makeShared(jsi::Function&& value) {
|
|
93
|
+
OwningReference<jsi::Function> owning(new jsi::Function(std::move(value)));
|
|
94
|
+
_strongCache->_functionCache.push_back(owning.weak());
|
|
95
|
+
return owning;
|
|
96
|
+
}
|
|
97
|
+
OwningReference<jsi::WeakObject> makeShared(jsi::WeakObject&& value) {
|
|
98
|
+
OwningReference<jsi::WeakObject> owning(new jsi::WeakObject(std::move(value)));
|
|
99
|
+
_strongCache->_weakObjectCache.push_back(owning.weak());
|
|
100
|
+
return owning;
|
|
101
|
+
}
|
|
102
|
+
OwningReference<jsi::ArrayBuffer> makeShared(jsi::ArrayBuffer&& value) {
|
|
103
|
+
OwningReference<jsi::ArrayBuffer> owning(new jsi::ArrayBuffer(std::move(value)));
|
|
104
|
+
_strongCache->_arrayBufferCache.push_back(owning.weak());
|
|
105
|
+
return owning;
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
private:
|
|
@@ -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
|
|
@@ -31,18 +31,30 @@ template <typename T>
|
|
|
31
31
|
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::MutableBuffer>>> {
|
|
32
32
|
static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
33
33
|
jsi::Object object = arg.asObject(runtime);
|
|
34
|
+
|
|
35
|
+
#if DEBUG
|
|
34
36
|
if (!object.isArrayBuffer(runtime)) [[unlikely]] {
|
|
35
|
-
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) +
|
|
37
|
+
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) +
|
|
38
|
+
"\" is not an ArrayBuffer! "
|
|
39
|
+
"Are you maybe passing a TypedArray (e.g. Uint8Array)? Try to pass it's `.buffer` value.");
|
|
36
40
|
}
|
|
41
|
+
#endif
|
|
37
42
|
|
|
38
43
|
JSICacheReference cache = JSICache::getOrCreateCache(runtime);
|
|
39
|
-
auto
|
|
44
|
+
auto borrowingArrayBuffer = cache.makeShared(object.getArrayBuffer(runtime));
|
|
40
45
|
|
|
41
|
-
return std::make_shared<JSArrayBuffer>(&runtime,
|
|
46
|
+
return std::make_shared<JSArrayBuffer>(&runtime, borrowingArrayBuffer);
|
|
42
47
|
}
|
|
43
48
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<jsi::MutableBuffer>& buffer) {
|
|
44
49
|
return jsi::ArrayBuffer(runtime, buffer);
|
|
45
50
|
}
|
|
51
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
52
|
+
if (value.isObject()) {
|
|
53
|
+
jsi::Object object = value.getObject(runtime);
|
|
54
|
+
return object.isArrayBuffer(runtime);
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
46
58
|
};
|
|
47
59
|
|
|
48
60
|
} // namespace margelo::nitro
|
|
@@ -35,7 +35,7 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
35
35
|
// Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
|
|
36
36
|
auto cache = JSICache::getOrCreateCache(runtime);
|
|
37
37
|
jsi::Function function = arg.asObject(runtime).asFunction(runtime);
|
|
38
|
-
OwningReference<jsi::Function> sharedFunction = cache.
|
|
38
|
+
OwningReference<jsi::Function> sharedFunction = cache.makeShared(std::move(function));
|
|
39
39
|
|
|
40
40
|
std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
41
41
|
std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
|
|
@@ -79,6 +79,14 @@ struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
|
79
79
|
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "hostFunction"), sizeof...(Args), jsFunction);
|
|
80
80
|
}
|
|
81
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
|
+
|
|
82
90
|
private:
|
|
83
91
|
static inline ResultingType callJSFunction(jsi::Runtime& runtime, const OwningReference<jsi::Function>& function, const Args&... args) {
|
|
84
92
|
// Throw a lock on the OwningReference<T> so we can guarantee safe access (Hermes GC cannot delete it while `lock` is alive)
|
|
@@ -7,117 +7,72 @@
|
|
|
7
7
|
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
8
|
namespace margelo::nitro {
|
|
9
9
|
class HybridObject;
|
|
10
|
-
|
|
11
|
-
template <typename T, typename Enable>
|
|
12
|
-
struct JSIConverter;
|
|
13
10
|
} // namespace margelo::nitro
|
|
14
11
|
|
|
15
|
-
#include "JSIConverter.hpp"
|
|
16
|
-
|
|
17
|
-
#include "HybridObject.hpp"
|
|
18
12
|
#include "IsSharedPtrTo.hpp"
|
|
19
13
|
#include "TypeInfo.hpp"
|
|
20
14
|
#include <jsi/jsi.h>
|
|
21
|
-
#include <memory>
|
|
22
15
|
#include <type_traits>
|
|
23
16
|
|
|
24
|
-
#define DO_NULL_CHECKS true
|
|
25
|
-
|
|
26
17
|
namespace margelo::nitro {
|
|
27
18
|
|
|
28
19
|
using namespace facebook;
|
|
29
20
|
|
|
30
|
-
// HybridObject <> {}
|
|
21
|
+
// HybridObject(NativeState) <> {}
|
|
31
22
|
template <typename T>
|
|
32
|
-
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::
|
|
23
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> {
|
|
33
24
|
using TPointee = typename T::element_type;
|
|
34
25
|
|
|
35
26
|
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
36
|
-
#if
|
|
37
|
-
if (arg.isUndefined()) [[unlikely]] {
|
|
38
|
-
throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
|
|
39
|
-
}
|
|
27
|
+
#if DEBUG
|
|
40
28
|
if (!arg.isObject()) [[unlikely]] {
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
if (arg.isUndefined()) [[unlikely]] {
|
|
30
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
|
|
31
|
+
} else {
|
|
32
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
33
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not an object!"));
|
|
34
|
+
}
|
|
43
35
|
}
|
|
44
36
|
#endif
|
|
45
37
|
jsi::Object object = arg.asObject(runtime);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
|
|
39
|
+
#if DEBUG
|
|
40
|
+
if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
|
|
41
|
+
if (!object.hasNativeState(runtime)) [[unlikely]] {
|
|
42
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
43
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It does not have a NativeState!"));
|
|
44
|
+
} else {
|
|
45
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
46
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It has a different NativeState<T>!"));
|
|
47
|
+
}
|
|
54
48
|
}
|
|
55
49
|
#endif
|
|
56
|
-
return object.
|
|
50
|
+
return object.getNativeState<TPointee>(runtime);
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
|
|
60
|
-
#if DO_NULL_CHECKS
|
|
61
54
|
if (arg == nullptr) [[unlikely]] {
|
|
62
55
|
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
63
|
-
throw jsi::JSError(runtime, "Cannot convert nullptr to
|
|
56
|
+
throw jsi::JSError(runtime, "Cannot convert nullptr to NativeState<" + typeName + ">!");
|
|
64
57
|
}
|
|
65
|
-
|
|
58
|
+
|
|
66
59
|
if constexpr (std::is_base_of_v<HybridObject, TPointee>) {
|
|
67
60
|
// It's a HybridObject - use it's internal constructor which caches jsi::Objects for proper memory management!
|
|
68
61
|
return arg->toObject(runtime);
|
|
69
62
|
} else {
|
|
70
|
-
// It's any other kind of jsi::HostObject - just create it as normal.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
private:
|
|
76
|
-
static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
|
|
77
|
-
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
78
|
-
return "Cannot convert \"" + typeDescription + "\" to HostObject<" + typeName + ">! " + reason;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// NativeState <> {}
|
|
83
|
-
template <typename T>
|
|
84
|
-
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_v<T, jsi::NativeState>>> {
|
|
85
|
-
using TPointee = typename T::element_type;
|
|
86
|
-
|
|
87
|
-
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
88
|
-
#if DO_NULL_CHECKS
|
|
89
|
-
if (arg.isUndefined()) [[unlikely]] {
|
|
90
|
-
throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
|
|
91
|
-
}
|
|
92
|
-
if (!arg.isObject()) [[unlikely]] {
|
|
93
|
-
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
94
|
-
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not an object!"));
|
|
95
|
-
}
|
|
96
|
-
#endif
|
|
97
|
-
jsi::Object object = arg.asObject(runtime);
|
|
98
|
-
#if DO_NULL_CHECKS
|
|
99
|
-
if (!object.hasNativeState(runtime)) [[unlikely]] {
|
|
100
|
-
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
101
|
-
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not a NativeState!"));
|
|
102
|
-
}
|
|
103
|
-
if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
|
|
104
|
-
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
105
|
-
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different NativeState<T>!"));
|
|
63
|
+
// It's any other kind of jsi::HostObject - just create it as normal. This will not have a prototype then!
|
|
64
|
+
jsi::Object object(runtime);
|
|
65
|
+
object.setNativeState(runtime, arg);
|
|
66
|
+
return object;
|
|
106
67
|
}
|
|
107
|
-
#endif
|
|
108
|
-
return object.getNativeState<TPointee>(runtime);
|
|
109
68
|
}
|
|
110
69
|
|
|
111
|
-
static inline
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
throw jsi::JSError(runtime, "Cannot convert nullptr to NativeState<" + typeName + ">!");
|
|
70
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
71
|
+
if (value.isObject()) {
|
|
72
|
+
jsi::Object object = value.getObject(runtime);
|
|
73
|
+
return object.hasNativeState<TPointee>(runtime);
|
|
116
74
|
}
|
|
117
|
-
|
|
118
|
-
jsi::Object object(runtime);
|
|
119
|
-
object.setNativeState(runtime, arg);
|
|
120
|
-
return object;
|
|
75
|
+
return false;
|
|
121
76
|
}
|
|
122
77
|
|
|
123
78
|
private:
|
|
@@ -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
|