1 #pragma once
2
3 #include <jsi/jsi.h>
4 #include <sstream>
5 #include <string>
6 #include <tuple>
7 #include <utility>
8
9 using namespace facebook;
10
11 namespace reanimated {
12 namespace jsi_utils {
13
14 // `get` functions take a pointer to `jsi::Value` and
15 // call an appropriate method to cast to the native type
16 template <typename T>
17 inline T get(jsi::Runtime &rt, const jsi::Value *value);
18
19 template <>
20 inline double get<double>(jsi::Runtime &rt, const jsi::Value *value) {
21 return value->asNumber();
22 }
23
24 template <>
25 inline int get<int>(jsi::Runtime &rt, const jsi::Value *value) {
26 return value->asNumber();
27 }
28
29 template <>
30 inline bool get<bool>(jsi::Runtime &rt, const jsi::Value *value) {
31 if (!value->isBool()) {
32 throw jsi::JSINativeException("Expected a boolean");
33 }
34 return value->getBool();
35 }
36
37 template <>
38 inline jsi::Object get<jsi::Object>(jsi::Runtime &rt, const jsi::Value *value) {
39 return value->asObject(rt);
40 }
41
42 template <>
43 inline jsi::Value const &get<jsi::Value const &>(
44 jsi::Runtime &rt,
45 const jsi::Value *value) {
46 return *value;
47 }
48
49 // `convertArgs` functions take a variadic template parameter of target (C++)
50 // argument types `Targs` and a `jsi::Value` array `args`, and converts `args`
51 // to a tuple of typed C++ arguments to be passed to the native implementation.
52 // This is accomplished by dispatching (at compile time) to the correct
53 // implementation based on the first type of `Targs`, using SFINAE to select the
54 // correct specialization, and concatenating with the result of recursion on the
55 // rest of `Targs`
56
57 // BEGIN implementations for `convertArgs` specializations.
58 // specialization for empty `Targs` - returns an empty tuple
59 template <typename... Args>
convertArgs(jsi::Runtime & rt,const jsi::Value * args)60 inline std::enable_if_t<(sizeof...(Args) == 0), std::tuple<>> convertArgs(
61 jsi::Runtime &rt,
62 const jsi::Value *args) {
63 return std::make_tuple();
64 }
65
66 // calls `get<First>` on the first argument to retrieve the native type,
67 // then calls recursively on the rest of `args`
68 // and returns the concatenation of results
69 template <typename T, typename... Rest>
convertArgs(jsi::Runtime & rt,const jsi::Value * args)70 inline std::tuple<T, Rest...> convertArgs(
71 jsi::Runtime &rt,
72 const jsi::Value *args) {
73 auto arg = std::tuple<T>(get<T>(rt, args));
74 auto rest = convertArgs<Rest...>(rt, std::next(args));
75 return std::tuple_cat(std::move(arg), std::move(rest));
76 }
77 // END implementations for `convertArgs` specializations.
78
79 // returns a tuple with the result of casting `args` to appropriate
80 // native C++ types needed to call `function`
81 template <typename Ret, typename... Args>
getArgsForFunction(std::function<Ret (Args...)> function,jsi::Runtime & rt,const jsi::Value * args,const size_t count)82 std::tuple<Args...> getArgsForFunction(
83 std::function<Ret(Args...)> function,
84 jsi::Runtime &rt,
85 const jsi::Value *args,
86 const size_t count) {
87 assert(sizeof...(Args) == count);
88 return convertArgs<Args...>(rt, args);
89 }
90
91 // returns a tuple with the result of casting `args` to appropriate
92 // native C++ types needed to call `function`,
93 // passing `rt` as the first argument
94 template <typename Ret, typename... Args>
getArgsForFunction(std::function<Ret (jsi::Runtime &,Args...)> function,jsi::Runtime & rt,const jsi::Value * args,const size_t count)95 std::tuple<jsi::Runtime &, Args...> getArgsForFunction(
96 std::function<Ret(jsi::Runtime &, Args...)> function,
97 jsi::Runtime &rt,
98 const jsi::Value *args,
99 const size_t count) {
100 assert(sizeof...(Args) == count);
101 return std::tuple_cat(std::tie(rt), convertArgs<Args...>(rt, args));
102 }
103
104 // calls `function` with `args`
105 template <typename Ret, typename... Args>
apply(std::function<Ret (Args...)> function,std::tuple<Args...> args)106 inline jsi::Value apply(
107 std::function<Ret(Args...)> function,
108 std::tuple<Args...> args) {
109 return std::apply(function, std::move(args));
110 }
111
112 // calls void-returning `function` with `args`,
113 // and returns `undefined`
114 template <typename... Args>
apply(std::function<void (Args...)> function,std::tuple<Args...> args)115 inline jsi::Value apply(
116 std::function<void(Args...)> function,
117 std::tuple<Args...> args) {
118 std::apply(function, std::move(args));
119 return jsi::Value::undefined();
120 }
121
122 // returns a function with JSI calling convention
123 // from a native function `function`
124 template <typename Fun>
createHostFunction(Fun function)125 jsi::HostFunctionType createHostFunction(Fun function) {
126 return [function](
127 jsi::Runtime &rt,
128 const jsi::Value &thisValue,
129 const jsi::Value *args,
130 const size_t count) {
131 auto argz = getArgsForFunction(function, rt, args, count);
132 return apply(function, std::move(argz));
133 };
134 }
135
136 // used to determine if `function<Ret(Args...)>`
137 // takes `Runtime &` as its first argument
138 template <typename... Args>
139 struct takes_runtime {
140 static constexpr size_t value = 0;
141 };
142
143 // specialization for `function<Ret(Runtime &, Rest...)`
144 template <typename... Rest>
145 struct takes_runtime<jsi::Runtime &, Rest...> {
146 static constexpr size_t value = 1;
147 };
148
149 // creates a JSI compatible function from `function`
150 // and installs it as a global function named `name`
151 // in the `rt` JS runtime
152 template <typename Ret, typename... Args>
153 void installJsiFunction(
154 jsi::Runtime &rt,
155 std::string_view name,
156 std::function<Ret(Args...)> function) {
157 auto clb = createHostFunction(function);
158 auto argsCount = sizeof...(Args) - takes_runtime<Args...>::value;
159 jsi::Value jsiFunction = jsi::Function::createFromHostFunction(
160 rt, jsi::PropNameID::forAscii(rt, name.data()), argsCount, clb);
161 rt.global().setProperty(rt, name.data(), jsiFunction);
162 }
163
164 // this should take care of passing types convertible to `function`
165 template <typename Fun>
166 void installJsiFunction(jsi::Runtime &rt, std::string_view name, Fun function) {
167 installJsiFunction(rt, name, std::function(std::forward<Fun>(function)));
168 }
169
170 jsi::Array convertStringToArray(
171 jsi::Runtime &rt,
172 const std::string &value,
173 const unsigned int expectedSize);
174
175 } // namespace jsi_utils
176 } // namespace reanimated
177