react-native-nitro-modules 0.1.3 → 0.1.4
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 +3 -1
- package/android/src/main/cpp/platform/ThreadUtils.cpp +5 -0
- package/cpp/core/AnyMap.hpp +2 -1
- package/cpp/core/HybridObject.hpp +3 -4
- package/cpp/jsi/JSIConverter+AnyMap.hpp +64 -0
- package/cpp/jsi/JSIConverter+ArrayBuffer.hpp +41 -0
- package/cpp/jsi/JSIConverter+Function.hpp +124 -0
- package/cpp/jsi/JSIConverter+HybridObject.hpp +123 -0
- package/cpp/jsi/JSIConverter+Optional.hpp +41 -0
- package/cpp/jsi/JSIConverter+Promise.hpp +87 -0
- package/cpp/jsi/JSIConverter+Tuple.hpp +60 -0
- package/cpp/jsi/JSIConverter+UnorderedMap.hpp +50 -0
- package/cpp/jsi/JSIConverter+Variant.hpp +97 -0
- package/cpp/jsi/JSIConverter+Vector.hpp +48 -0
- package/cpp/jsi/JSIConverter.hpp +22 -498
- package/cpp/platform/ThreadUtils.hpp +6 -0
- package/cpp/threading/ThreadPool.cpp +84 -0
- package/cpp/threading/ThreadPool.hpp +53 -0
- package/ios/core/HybridContext.cpp +8 -0
- package/{cpp → ios}/core/HybridContext.hpp +6 -3
- package/ios/platform/ThreadUtils.cpp +4 -0
- package/lib/NativeNitroModules.d.ts +5 -0
- package/lib/NativeNitroModules.js +5 -0
- package/lib/NativeNitroModules.web.d.ts +4 -0
- package/lib/NativeNitroModules.web.js +3 -0
- package/lib/commonjs/NativeNitroModules.js +6 -0
- package/lib/commonjs/NativeNitroModules.js.map +1 -1
- package/lib/commonjs/NativeNitroModules.web.js +10 -0
- package/lib/commonjs/NativeNitroModules.web.js.map +1 -0
- package/lib/module/NativeNitroModules.js +5 -0
- package/lib/module/NativeNitroModules.js.map +1 -1
- package/lib/module/NativeNitroModules.web.js +4 -0
- package/lib/module/NativeNitroModules.web.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/NativeNitroModules.ts +11 -0
- package/src/NativeNitroModules.web.ts +9 -0
package/NitroModules.podspec
CHANGED
|
@@ -28,14 +28,16 @@ Pod::Spec.new do |s|
|
|
|
28
28
|
]
|
|
29
29
|
s.public_header_files = [
|
|
30
30
|
# Public C++ headers will be exposed in modulemap (for Swift)
|
|
31
|
+
"cpp/core/AnyMap.hpp",
|
|
31
32
|
"cpp/core/ArrayBuffer.hpp",
|
|
32
33
|
"cpp/core/HybridObject.hpp",
|
|
33
|
-
"cpp/core/HybridContext.hpp",
|
|
34
34
|
"cpp/registry/HybridObjectRegistry.hpp",
|
|
35
35
|
"cpp/jsi/JSIConverter.hpp",
|
|
36
36
|
"cpp/threading/Dispatcher.hpp",
|
|
37
37
|
"cpp/utils/NitroHash.hpp",
|
|
38
38
|
"cpp/utils/NitroDefines.hpp",
|
|
39
|
+
# Public iOS-specific headers that will be exposed in modulemap (for Swift)
|
|
40
|
+
"ios/core/HybridContext.hpp"
|
|
39
41
|
]
|
|
40
42
|
|
|
41
43
|
s.pod_target_xcconfig = {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include <pthread.h>
|
|
10
10
|
#include <sstream>
|
|
11
11
|
#include <string>
|
|
12
|
+
#include <sys/prctl.h>
|
|
12
13
|
#include <thread>
|
|
13
14
|
|
|
14
15
|
namespace margelo::nitro {
|
|
@@ -32,4 +33,8 @@ std::string ThreadUtils::getThreadName() {
|
|
|
32
33
|
return "Thread #" + threadId;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
void ThreadUtils::setThreadName(const std::string& name) {
|
|
37
|
+
prctl(PR_SET_NAME, name.c_str(), 0, 0, 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
} // namespace margelo::nitro
|
package/cpp/core/AnyMap.hpp
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
#include <string>
|
|
10
10
|
#include <unordered_map>
|
|
11
11
|
#include <variant>
|
|
12
|
+
#include <vector>
|
|
12
13
|
|
|
13
14
|
namespace margelo::nitro {
|
|
14
15
|
|
|
@@ -33,7 +34,7 @@ struct AnyValue : VariantType {
|
|
|
33
34
|
* 2. Arrays of primitives
|
|
34
35
|
* 3. Objects of primitives
|
|
35
36
|
*/
|
|
36
|
-
class AnyMap {
|
|
37
|
+
class AnyMap final {
|
|
37
38
|
public:
|
|
38
39
|
/**
|
|
39
40
|
* Create a new instance of AnyMap.
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
#pragma once
|
|
6
6
|
|
|
7
7
|
#include "CountTrailingOptionals.hpp"
|
|
8
|
-
#include "HybridContext.hpp"
|
|
9
8
|
#include "JSIConverter.hpp"
|
|
10
|
-
#include "NitroLogger.hpp"
|
|
11
9
|
#include "OwningReference.hpp"
|
|
10
|
+
#include "TypeInfo.hpp"
|
|
12
11
|
#include <functional>
|
|
13
12
|
#include <jsi/jsi.h>
|
|
14
13
|
#include <memory>
|
|
@@ -176,10 +175,10 @@ private:
|
|
|
176
175
|
constexpr size_t maxArgsCount = sizeof...(Args);
|
|
177
176
|
constexpr size_t minArgsCount = maxArgsCount - optionalArgsCount;
|
|
178
177
|
bool isWithinArgsRange = (count >= minArgsCount && count <= maxArgsCount);
|
|
179
|
-
if (!isWithinArgsRange) {
|
|
178
|
+
if (!isWithinArgsRange) [[unlikely]] {
|
|
180
179
|
// invalid amount of arguments passed!
|
|
181
180
|
std::string hybridObjectName = derivedInstance->_name;
|
|
182
|
-
if (minArgsCount == maxArgsCount) {
|
|
181
|
+
if constexpr (minArgsCount == maxArgsCount) {
|
|
183
182
|
// min and max args length is the same, so we don't have any optional parameters. fixed count
|
|
184
183
|
throw jsi::JSError(runtime, hybridObjectName + "." + name + "(...) expected " + std::to_string(maxArgsCount) +
|
|
185
184
|
" arguments, but received " + std::to_string(count) + "!");
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
struct AnyValue;
|
|
10
|
+
class AnyMap;
|
|
11
|
+
|
|
12
|
+
template <typename T, typename Enable>
|
|
13
|
+
struct JSIConverter;
|
|
14
|
+
} // namespace margelo::nitro
|
|
15
|
+
|
|
16
|
+
#include "JSIConverter.hpp"
|
|
17
|
+
#include "JSIConverter+Variant.hpp"
|
|
18
|
+
|
|
19
|
+
#include "AnyMap.hpp"
|
|
20
|
+
#include <jsi/jsi.h>
|
|
21
|
+
#include <memory>
|
|
22
|
+
|
|
23
|
+
namespace margelo::nitro {
|
|
24
|
+
|
|
25
|
+
using namespace facebook;
|
|
26
|
+
|
|
27
|
+
// AnyValue <> Record<K, V>
|
|
28
|
+
template <>
|
|
29
|
+
struct JSIConverter<AnyValue> {
|
|
30
|
+
static inline AnyValue fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
31
|
+
return JSIConverter<AnyValue::variant>::fromJSI(runtime, arg);
|
|
32
|
+
}
|
|
33
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const AnyValue& value) {
|
|
34
|
+
return JSIConverter<AnyValue::variant>::toJSI(runtime, value);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// AnyMap <> Record<K, V>
|
|
39
|
+
template <>
|
|
40
|
+
struct JSIConverter<std::shared_ptr<AnyMap>> {
|
|
41
|
+
static inline std::shared_ptr<AnyMap> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
42
|
+
jsi::Object object = arg.asObject(runtime);
|
|
43
|
+
jsi::Array propNames = object.getPropertyNames(runtime);
|
|
44
|
+
size_t size = propNames.size(runtime);
|
|
45
|
+
std::shared_ptr<AnyMap> map = AnyMap::make();
|
|
46
|
+
for (size_t i = 0; i < size; i++) {
|
|
47
|
+
jsi::String jsKey = propNames.getValueAtIndex(runtime, i).getString(runtime);
|
|
48
|
+
jsi::Value jsValue = object.getProperty(runtime, jsKey);
|
|
49
|
+
map->setAny(jsKey.utf8(runtime), JSIConverter<AnyValue>::fromJSI(runtime, jsValue));
|
|
50
|
+
}
|
|
51
|
+
return map;
|
|
52
|
+
}
|
|
53
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<AnyMap> map) {
|
|
54
|
+
jsi::Object object(runtime);
|
|
55
|
+
for (const auto& item : map->getMap()) {
|
|
56
|
+
jsi::String key = jsi::String::createFromUtf8(runtime, item.first);
|
|
57
|
+
jsi::Value value = JSIConverter<AnyValue>::toJSI(runtime, item.second);
|
|
58
|
+
object.setProperty(runtime, std::move(key), std::move(value));
|
|
59
|
+
}
|
|
60
|
+
return object;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
class ArrayBuffer;
|
|
10
|
+
|
|
11
|
+
template <typename T, typename Enable>
|
|
12
|
+
struct JSIConverter;
|
|
13
|
+
} // namespace margelo::nitro
|
|
14
|
+
|
|
15
|
+
#include "JSIConverter.hpp"
|
|
16
|
+
|
|
17
|
+
#include "ArrayBuffer.hpp"
|
|
18
|
+
#include <jsi/jsi.h>
|
|
19
|
+
#include <memory>
|
|
20
|
+
|
|
21
|
+
namespace margelo::nitro {
|
|
22
|
+
|
|
23
|
+
using namespace facebook;
|
|
24
|
+
|
|
25
|
+
// MutableBuffer <> ArrayBuffer
|
|
26
|
+
template <>
|
|
27
|
+
struct JSIConverter<std::shared_ptr<jsi::MutableBuffer>> {
|
|
28
|
+
static inline std::shared_ptr<ArrayBuffer> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
29
|
+
jsi::Object object = arg.asObject(runtime);
|
|
30
|
+
if (!object.isArrayBuffer(runtime)) [[unlikely]] {
|
|
31
|
+
throw std::runtime_error("Object \"" + arg.toString(runtime).utf8(runtime) + "\" is not an ArrayBuffer!");
|
|
32
|
+
}
|
|
33
|
+
jsi::ArrayBuffer arrayBuffer = object.getArrayBuffer(runtime);
|
|
34
|
+
return std::make_shared<ArrayBuffer>(arrayBuffer.data(runtime), arrayBuffer.size(runtime), false);
|
|
35
|
+
}
|
|
36
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::shared_ptr<jsi::MutableBuffer> buffer) {
|
|
37
|
+
return jsi::ArrayBuffer(runtime, buffer);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
template <typename T>
|
|
10
|
+
class JSICache;
|
|
11
|
+
|
|
12
|
+
template <typename T, typename Enable>
|
|
13
|
+
struct JSIConverter;
|
|
14
|
+
} // namespace margelo::nitro
|
|
15
|
+
|
|
16
|
+
#include "JSIConverter.hpp"
|
|
17
|
+
|
|
18
|
+
#include "Dispatcher.hpp"
|
|
19
|
+
#include "FutureType.hpp"
|
|
20
|
+
#include "JSICache.hpp"
|
|
21
|
+
#include <functional>
|
|
22
|
+
#include <future>
|
|
23
|
+
#include <jsi/jsi.h>
|
|
24
|
+
|
|
25
|
+
namespace margelo::nitro {
|
|
26
|
+
|
|
27
|
+
using namespace facebook;
|
|
28
|
+
|
|
29
|
+
// [](Args...) -> T {} <> (Args...) => T
|
|
30
|
+
template <typename ReturnType, typename... Args>
|
|
31
|
+
struct JSIConverter<std::function<ReturnType(Args...)>> {
|
|
32
|
+
// std::future<T> -> T
|
|
33
|
+
using ResultingType = future_type_v<ReturnType>;
|
|
34
|
+
|
|
35
|
+
static inline std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
36
|
+
// 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<jsi::Function>::getOrCreateCache(runtime);
|
|
38
|
+
jsi::Function function = arg.asObject(runtime).asFunction(runtime);
|
|
39
|
+
OwningReference<jsi::Function> sharedFunction = cache.makeGlobal(std::move(function));
|
|
40
|
+
|
|
41
|
+
std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
42
|
+
std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
|
|
43
|
+
|
|
44
|
+
// Create a C++ function that can be called by the consumer.
|
|
45
|
+
// This will call the jsi::Function if it is still alive.
|
|
46
|
+
return [&runtime, weakDispatcher, sharedFunction = std::move(sharedFunction)](Args... args) -> ReturnType {
|
|
47
|
+
// Try to get the JS Dispatcher if the Runtime is still alive
|
|
48
|
+
std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
|
|
49
|
+
if (!dispatcher) {
|
|
50
|
+
if constexpr (std::is_void_v<ResultingType>) {
|
|
51
|
+
Logger::log("JSIConverter", "Tried calling void(..) function, but the JS Dispatcher has already been deleted by JS!");
|
|
52
|
+
return;
|
|
53
|
+
} else {
|
|
54
|
+
throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if constexpr (std::is_void_v<ResultingType>) {
|
|
59
|
+
dispatcher->runAsync([&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() {
|
|
60
|
+
callJSFunction(runtime, sharedFunction, args...);
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
return dispatcher->runAsyncAwaitable<ResultingType>(
|
|
64
|
+
[&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() -> ResultingType {
|
|
65
|
+
return callJSFunction(runtime, sharedFunction, args...);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::function<ReturnType(Args...)>&& function) {
|
|
72
|
+
jsi::HostFunctionType jsFunction = [function = std::move(function)](jsi::Runtime& runtime, const jsi::Value& thisValue,
|
|
73
|
+
const jsi::Value* args, size_t count) -> jsi::Value {
|
|
74
|
+
if (count != sizeof...(Args)) [[unlikely]] {
|
|
75
|
+
throw jsi::JSError(runtime, "Function expected " + std::to_string(sizeof...(Args)) + " arguments, but received " +
|
|
76
|
+
std::to_string(count) + "!");
|
|
77
|
+
}
|
|
78
|
+
return callHybridFunction(function, runtime, args, std::index_sequence_for<Args...>{});
|
|
79
|
+
};
|
|
80
|
+
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "hostFunction"), sizeof...(Args), jsFunction);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private:
|
|
84
|
+
static inline ResultingType callJSFunction(jsi::Runtime& runtime, const OwningReference<jsi::Function>& function, const Args&... args) {
|
|
85
|
+
// Throw a lock on the OwningReference<T> so we can guarantee safe access (Hermes GC cannot delete it while `lock` is alive)
|
|
86
|
+
OwningLock<jsi::Function> lock = function.lock();
|
|
87
|
+
|
|
88
|
+
if (!function) {
|
|
89
|
+
if constexpr (std::is_void_v<ResultingType>) {
|
|
90
|
+
// runtime has already been deleted. since this returns void, we can just ignore it being deleted.
|
|
91
|
+
Logger::log("JSIConverter", "Tried calling void(..) function, but it has already been deleted by JS!");
|
|
92
|
+
return;
|
|
93
|
+
} else {
|
|
94
|
+
// runtime has already been deleted, but we are expecting a return value - throw an error in this case.
|
|
95
|
+
throw std::runtime_error("Cannot call the given Function - the JS Dispatcher has already been destroyed by the JS Runtime!");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if constexpr (std::is_void_v<ResultingType>) {
|
|
100
|
+
// It returns void. Just call the function
|
|
101
|
+
function->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
|
|
102
|
+
} else {
|
|
103
|
+
// It returns some kind of value - call the function, and convert the return value.
|
|
104
|
+
jsi::Value result = function->call(runtime, JSIConverter<std::decay_t<Args>>::toJSI(runtime, args)...);
|
|
105
|
+
return JSIConverter<ResultingType>::fromJSI(runtime, std::move(result));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
template <size_t... Is>
|
|
110
|
+
static inline jsi::Value callHybridFunction(const std::function<ReturnType(Args...)>& function, jsi::Runtime& runtime,
|
|
111
|
+
const jsi::Value* args, std::index_sequence<Is...>) {
|
|
112
|
+
if constexpr (std::is_void_v<ReturnType>) {
|
|
113
|
+
// it is a void function (will return undefined in JS)
|
|
114
|
+
function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
|
|
115
|
+
return jsi::Value::undefined();
|
|
116
|
+
} else {
|
|
117
|
+
// it is a custom type, parse it to a JS value
|
|
118
|
+
ReturnType result = function(JSIConverter<std::decay_t<Args>>::fromJSI(runtime, args[Is])...);
|
|
119
|
+
return JSIConverter<ReturnType>::toJSI(runtime, result);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
class HybridObject;
|
|
10
|
+
|
|
11
|
+
template <typename T, typename Enable>
|
|
12
|
+
struct JSIConverter;
|
|
13
|
+
} // namespace margelo::nitro
|
|
14
|
+
|
|
15
|
+
#include "JSIConverter.hpp"
|
|
16
|
+
|
|
17
|
+
#include "HybridObject.hpp"
|
|
18
|
+
#include "IsHostObject.hpp"
|
|
19
|
+
#include "IsNativeState.hpp"
|
|
20
|
+
#include "TypeInfo.hpp"
|
|
21
|
+
#include <jsi/jsi.h>
|
|
22
|
+
#include <memory>
|
|
23
|
+
#include <type_traits>
|
|
24
|
+
|
|
25
|
+
#define DO_NULL_CHECKS true
|
|
26
|
+
|
|
27
|
+
namespace margelo::nitro {
|
|
28
|
+
|
|
29
|
+
using namespace facebook;
|
|
30
|
+
|
|
31
|
+
// HybridObject <> {}
|
|
32
|
+
template <typename T>
|
|
33
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_host_object_v<T>>> {
|
|
34
|
+
using TPointee = typename T::element_type;
|
|
35
|
+
|
|
36
|
+
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
37
|
+
#if DO_NULL_CHECKS
|
|
38
|
+
if (arg.isUndefined()) [[unlikely]] {
|
|
39
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
|
|
40
|
+
}
|
|
41
|
+
if (!arg.isObject()) [[unlikely]] {
|
|
42
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
43
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not an object!"));
|
|
44
|
+
}
|
|
45
|
+
#endif
|
|
46
|
+
jsi::Object object = arg.asObject(runtime);
|
|
47
|
+
#if DO_NULL_CHECKS
|
|
48
|
+
if (!object.isHostObject<TPointee>(runtime)) [[unlikely]] {
|
|
49
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
50
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different HostObject<T>!"));
|
|
51
|
+
}
|
|
52
|
+
#endif
|
|
53
|
+
return object.asHostObject<TPointee>(runtime);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
|
|
57
|
+
#if DO_NULL_CHECKS
|
|
58
|
+
if (arg == nullptr) [[unlikely]] {
|
|
59
|
+
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
60
|
+
throw jsi::JSError(runtime, "Cannot convert nullptr to HostObject<" + typeName + ">!");
|
|
61
|
+
}
|
|
62
|
+
#endif
|
|
63
|
+
if constexpr (std::is_base_of_v<HybridObject, TPointee>) {
|
|
64
|
+
// It's a HybridObject - use it's internal constructor which caches jsi::Objects for proper memory management!
|
|
65
|
+
return arg->toObject(runtime);
|
|
66
|
+
} else {
|
|
67
|
+
// It's any other kind of jsi::HostObject - just create it as normal.
|
|
68
|
+
return jsi::Object::createFromHostObject(runtime, arg);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private:
|
|
73
|
+
static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
|
|
74
|
+
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
75
|
+
return "Cannot convert \"" + typeDescription + "\" to HostObject<" + typeName + ">! " + reason;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// NativeState <> {}
|
|
80
|
+
template <typename T>
|
|
81
|
+
struct JSIConverter<T, std::enable_if_t<is_shared_ptr_to_native_state_v<T>>> {
|
|
82
|
+
using TPointee = typename T::element_type;
|
|
83
|
+
|
|
84
|
+
static inline T fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
85
|
+
#if DO_NULL_CHECKS
|
|
86
|
+
if (arg.isUndefined()) [[unlikely]] {
|
|
87
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage("undefined", "It is undefined!"));
|
|
88
|
+
}
|
|
89
|
+
if (!arg.isObject()) [[unlikely]] {
|
|
90
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
91
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is not an object!"));
|
|
92
|
+
}
|
|
93
|
+
#endif
|
|
94
|
+
jsi::Object object = arg.asObject(runtime);
|
|
95
|
+
#if DO_NULL_CHECKS
|
|
96
|
+
if (!object.hasNativeState<TPointee>(runtime)) [[unlikely]] {
|
|
97
|
+
std::string stringRepresentation = arg.toString(runtime).utf8(runtime);
|
|
98
|
+
throw jsi::JSError(runtime, invalidTypeErrorMessage(stringRepresentation, "It is a different NativeState<T>!"));
|
|
99
|
+
}
|
|
100
|
+
#endif
|
|
101
|
+
return object.getNativeState<TPointee>(runtime);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const T& arg) {
|
|
105
|
+
#if DO_NULL_CHECKS
|
|
106
|
+
if (arg == nullptr) [[unlikely]] {
|
|
107
|
+
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
108
|
+
throw jsi::JSError(runtime, "Cannot convert nullptr to NativeState<" + typeName + ">!");
|
|
109
|
+
}
|
|
110
|
+
#endif
|
|
111
|
+
jsi::Object object(runtime);
|
|
112
|
+
object.setNativeState(runtime, arg);
|
|
113
|
+
return object;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private:
|
|
117
|
+
static inline std::string invalidTypeErrorMessage(const std::string& typeDescription, const std::string& reason) {
|
|
118
|
+
std::string typeName = TypeInfo::getFriendlyTypename<TPointee>();
|
|
119
|
+
return "Cannot convert \"" + typeDescription + "\" to NativeState<" + typeName + ">! " + reason;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
template <typename T, typename Enable>
|
|
10
|
+
struct JSIConverter;
|
|
11
|
+
} // namespace margelo::nitro
|
|
12
|
+
|
|
13
|
+
#include "JSIConverter.hpp"
|
|
14
|
+
|
|
15
|
+
#include <jsi/jsi.h>
|
|
16
|
+
#include <optional>
|
|
17
|
+
|
|
18
|
+
namespace margelo::nitro {
|
|
19
|
+
|
|
20
|
+
using namespace facebook;
|
|
21
|
+
|
|
22
|
+
// std::optional<T> <> T | undefined
|
|
23
|
+
template <typename TInner>
|
|
24
|
+
struct JSIConverter<std::optional<TInner>> {
|
|
25
|
+
static inline std::optional<TInner> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
26
|
+
if (arg.isUndefined() || arg.isNull()) {
|
|
27
|
+
return std::nullopt;
|
|
28
|
+
} else {
|
|
29
|
+
return JSIConverter<TInner>::fromJSI(runtime, arg);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::optional<TInner>& arg) {
|
|
33
|
+
if (arg == std::nullopt) {
|
|
34
|
+
return jsi::Value::undefined();
|
|
35
|
+
} else {
|
|
36
|
+
return JSIConverter<TInner>::toJSI(runtime, arg.value());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
class Dispatcher;
|
|
10
|
+
class Promise;
|
|
11
|
+
|
|
12
|
+
template <typename T, typename Enable>
|
|
13
|
+
struct JSIConverter;
|
|
14
|
+
} // namespace margelo::nitro
|
|
15
|
+
|
|
16
|
+
#include "JSIConverter.hpp"
|
|
17
|
+
|
|
18
|
+
#include "Dispatcher.hpp"
|
|
19
|
+
#include "Promise.hpp"
|
|
20
|
+
#include "ThreadPool.hpp"
|
|
21
|
+
#include "TypeInfo.hpp"
|
|
22
|
+
#include <future>
|
|
23
|
+
#include <jsi/jsi.h>
|
|
24
|
+
#include <memory>
|
|
25
|
+
|
|
26
|
+
namespace margelo::nitro {
|
|
27
|
+
|
|
28
|
+
using namespace facebook;
|
|
29
|
+
|
|
30
|
+
// std::future<T> <> Promise<T>
|
|
31
|
+
template <typename TResult>
|
|
32
|
+
struct JSIConverter<std::future<TResult>> {
|
|
33
|
+
static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
|
|
34
|
+
throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
|
|
35
|
+
}
|
|
36
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, std::future<TResult>&& arg) {
|
|
37
|
+
auto sharedFuture = std::make_shared<std::future<TResult>>(std::move(arg));
|
|
38
|
+
std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
39
|
+
std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
|
|
40
|
+
|
|
41
|
+
return Promise::createPromise(runtime, [sharedFuture, weakDispatcher](jsi::Runtime& runtime, std::shared_ptr<Promise> promise) {
|
|
42
|
+
// Spawn new async thread to synchronously wait for the `future<T>` to complete
|
|
43
|
+
std::shared_ptr<ThreadPool> pool = ThreadPool::getSharedPool();
|
|
44
|
+
pool->run([promise, &runtime, weakDispatcher, sharedFuture]() {
|
|
45
|
+
// synchronously wait until the `future<T>` completes. we are running on a background task here.
|
|
46
|
+
sharedFuture->wait();
|
|
47
|
+
|
|
48
|
+
// the async function completed successfully, get a JS Dispatcher so we can resolve on JS Thread
|
|
49
|
+
std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
|
|
50
|
+
if (!dispatcher) {
|
|
51
|
+
Logger::log("JSIConverter", "Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed.");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
dispatcher->runAsync([&runtime, promise, sharedFuture]() mutable {
|
|
56
|
+
try {
|
|
57
|
+
if constexpr (std::is_void_v<TResult>) {
|
|
58
|
+
// it's returning void, just return undefined to JS
|
|
59
|
+
sharedFuture->get();
|
|
60
|
+
promise->resolve(runtime, jsi::Value::undefined());
|
|
61
|
+
} else {
|
|
62
|
+
// it's returning a custom type, convert it to a jsi::Value
|
|
63
|
+
TResult result = sharedFuture->get();
|
|
64
|
+
jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, result);
|
|
65
|
+
promise->resolve(runtime, std::move(jsResult));
|
|
66
|
+
}
|
|
67
|
+
} catch (const std::exception& exception) {
|
|
68
|
+
// the async function threw an error, reject the promise on JS Thread
|
|
69
|
+
std::string what = exception.what();
|
|
70
|
+
promise->reject(runtime, what);
|
|
71
|
+
} catch (...) {
|
|
72
|
+
// the async function threw a non-std error, try getting it
|
|
73
|
+
std::string name = TypeInfo::getCurrentExceptionName();
|
|
74
|
+
promise->reject(runtime, "Unknown non-std exception: " + name);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// This lambda owns the promise shared pointer, and we need to call its
|
|
78
|
+
// destructor on the correct thread here - otherwise it might be called
|
|
79
|
+
// from the waiterThread.
|
|
80
|
+
promise = nullptr;
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
template <typename T, typename Enable>
|
|
10
|
+
struct JSIConverter;
|
|
11
|
+
} // namespace margelo::nitro
|
|
12
|
+
|
|
13
|
+
#include "JSIConverter.hpp"
|
|
14
|
+
|
|
15
|
+
#include "TypeInfo.hpp"
|
|
16
|
+
#include <jsi/jsi.h>
|
|
17
|
+
#include <memory>
|
|
18
|
+
#include <tuple>
|
|
19
|
+
|
|
20
|
+
namespace margelo::nitro {
|
|
21
|
+
|
|
22
|
+
using namespace facebook;
|
|
23
|
+
|
|
24
|
+
// std::tuple<A, B, C> <> [A, B, C]
|
|
25
|
+
template <typename... Types>
|
|
26
|
+
struct JSIConverter<std::tuple<Types...>> {
|
|
27
|
+
static inline std::tuple<Types...> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
28
|
+
jsi::Object object = value.asObject(runtime);
|
|
29
|
+
jsi::Array array = object.asArray(runtime);
|
|
30
|
+
if (array.size(runtime) != sizeof...(Types)) [[unlikely]] {
|
|
31
|
+
std::string types = TypeInfo::getFriendlyTypenames<Types...>();
|
|
32
|
+
throw std::runtime_error("The given JS Array has " + std::to_string(array.size(runtime)) + " items, but std::tuple<" + types +
|
|
33
|
+
"> expects " + std::to_string(sizeof...(Types)) + " items.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return copyArrayItemsToTuple(runtime, array, std::index_sequence_for<Types...>{});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::tuple<Types...>& tuple) {
|
|
40
|
+
jsi::Array array(runtime, sizeof...(Types));
|
|
41
|
+
copyTupleItemsToArray(runtime, array, tuple, std::index_sequence_for<Types...>{});
|
|
42
|
+
return array;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private:
|
|
46
|
+
template <std::size_t... Is>
|
|
47
|
+
static inline std::tuple<Types...> copyArrayItemsToTuple(jsi::Runtime& runtime, const jsi::Array& array, std::index_sequence<Is...>) {
|
|
48
|
+
return std::make_tuple(JSIConverter<Types>::fromJSI(runtime, array.getValueAtIndex(runtime, Is))...);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
template <std::size_t... Is>
|
|
52
|
+
static inline void copyTupleItemsToArray(jsi::Runtime& runtime, jsi::Array& array, const std::tuple<Types...>& tuple,
|
|
53
|
+
std::index_sequence<Is...>) {
|
|
54
|
+
((array.setValueAtIndex(runtime, Is,
|
|
55
|
+
JSIConverter<std::tuple_element_t<Is, std::tuple<Types...>>>::toJSI(runtime, std::get<Is>(tuple)))),
|
|
56
|
+
...);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Marc Rousavy on 21.02.24.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
// Forward declare a few of the common types that might have cyclic includes.
|
|
8
|
+
namespace margelo::nitro {
|
|
9
|
+
template <typename T, typename Enable>
|
|
10
|
+
struct JSIConverter;
|
|
11
|
+
} // namespace margelo::nitro
|
|
12
|
+
|
|
13
|
+
#include "JSIConverter.hpp"
|
|
14
|
+
|
|
15
|
+
#include "AnyMap.hpp"
|
|
16
|
+
#include <jsi/jsi.h>
|
|
17
|
+
#include <unordered_map>
|
|
18
|
+
|
|
19
|
+
namespace margelo::nitro {
|
|
20
|
+
|
|
21
|
+
using namespace facebook;
|
|
22
|
+
|
|
23
|
+
// std::unordered_map<std::string, T> <> Record<string, T>
|
|
24
|
+
template <typename ValueType>
|
|
25
|
+
struct JSIConverter<std::unordered_map<std::string, ValueType>> {
|
|
26
|
+
static inline std::unordered_map<std::string, ValueType> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
27
|
+
jsi::Object object = arg.asObject(runtime);
|
|
28
|
+
jsi::Array propertyNames = object.getPropertyNames(runtime);
|
|
29
|
+
size_t length = propertyNames.size(runtime);
|
|
30
|
+
|
|
31
|
+
std::unordered_map<std::string, ValueType> map;
|
|
32
|
+
map.reserve(length);
|
|
33
|
+
for (size_t i = 0; i < length; ++i) {
|
|
34
|
+
std::string key = propertyNames.getValueAtIndex(runtime, i).asString(runtime).utf8(runtime);
|
|
35
|
+
jsi::Value value = object.getProperty(runtime, key.c_str());
|
|
36
|
+
map.emplace(key, JSIConverter<ValueType>::fromJSI(runtime, value));
|
|
37
|
+
}
|
|
38
|
+
return map;
|
|
39
|
+
}
|
|
40
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::unordered_map<std::string, ValueType>& map) {
|
|
41
|
+
jsi::Object object(runtime);
|
|
42
|
+
for (const auto& pair : map) {
|
|
43
|
+
jsi::Value value = JSIConverter<ValueType>::toJSI(runtime, pair.second);
|
|
44
|
+
object.setProperty(runtime, pair.first.c_str(), std::move(value));
|
|
45
|
+
}
|
|
46
|
+
return object;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
} // namespace margelo::nitro
|