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> 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> 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> 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> 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> 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> 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> 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