1 /* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8 #pragma once 9 10 #include <ABI49_0_0React/bridging/ABI49_0_0Base.h> 11 #include <ABI49_0_0React/bridging/ABI49_0_0CallbackWrapper.h> 12 13 #include <ABI49_0_0ReactCommon/ABI49_0_0SchedulerPriority.h> 14 15 #include <ABI49_0_0butter/ABI49_0_0function.h> 16 17 namespace ABI49_0_0facebook::ABI49_0_0React { 18 19 template <typename F> 20 class SyncCallback; 21 22 template <typename... Args> 23 class AsyncCallback { 24 public: AsyncCallback(jsi::Runtime & runtime,jsi::Function function,std::shared_ptr<CallInvoker> jsInvoker)25 AsyncCallback( 26 jsi::Runtime &runtime, 27 jsi::Function function, 28 std::shared_ptr<CallInvoker> jsInvoker) 29 : callback_(std::make_shared<SyncCallback<void(Args...)>>( 30 runtime, 31 std::move(function), 32 std::move(jsInvoker))) {} 33 34 AsyncCallback(const AsyncCallback &) = default; 35 AsyncCallback &operator=(const AsyncCallback &) = default; 36 operator()37 void operator()(Args... args) const { 38 call(std::forward<Args>(args)...); 39 } 40 call(Args...args)41 void call(Args... args) const { 42 callInternal(std::nullopt, std::forward<Args>(args)...); 43 } 44 callWithPriority(SchedulerPriority priority,Args...args)45 void callWithPriority(SchedulerPriority priority, Args... args) const { 46 callInternal(priority, std::forward<Args>(args)...); 47 } 48 49 private: 50 friend Bridging<AsyncCallback>; 51 52 std::shared_ptr<SyncCallback<void(Args...)>> callback_; 53 callInternal(std::optional<SchedulerPriority> priority,Args...args)54 void callInternal(std::optional<SchedulerPriority> priority, Args... args) 55 const { 56 auto wrapper = callback_->wrapper_.lock(); 57 if (!wrapper) { 58 throw std::runtime_error("Failed to call invalidated async callback"); 59 } 60 auto fn = [callback = callback_, 61 argsPtr = std::make_shared<std::tuple<Args...>>( 62 std::make_tuple(std::forward<Args>(args)...))] { 63 callback->apply(std::move(*argsPtr)); 64 }; 65 66 if (priority) { 67 wrapper->jsInvoker().invokeAsync(*priority, std::move(fn)); 68 } else { 69 wrapper->jsInvoker().invokeAsync(std::move(fn)); 70 } 71 } 72 }; 73 74 template <typename R, typename... Args> 75 class SyncCallback<R(Args...)> { 76 public: SyncCallback(jsi::Runtime & rt,jsi::Function function,std::shared_ptr<CallInvoker> jsInvoker)77 SyncCallback( 78 jsi::Runtime &rt, 79 jsi::Function function, 80 std::shared_ptr<CallInvoker> jsInvoker) 81 : wrapper_(CallbackWrapper::createWeak( 82 std::move(function), 83 rt, 84 std::move(jsInvoker))) {} 85 86 // Disallow moving to prevent function from get called on another thread. 87 SyncCallback(SyncCallback &&) = delete; 88 SyncCallback &operator=(SyncCallback &&) = delete; 89 ~SyncCallback()90 ~SyncCallback() { 91 if (auto wrapper = wrapper_.lock()) { 92 wrapper->destroy(); 93 } 94 } 95 operator()96 R operator()(Args... args) const { 97 return call(std::forward<Args>(args)...); 98 } 99 call(Args...args)100 R call(Args... args) const { 101 auto wrapper = wrapper_.lock(); 102 if (!wrapper) { 103 throw std::runtime_error("Failed to call invalidated sync callback"); 104 } 105 106 auto &callback = wrapper->callback(); 107 auto &rt = wrapper->runtime(); 108 auto jsInvoker = wrapper->jsInvokerPtr(); 109 110 if constexpr (std::is_void_v<R>) { 111 callback.call( 112 rt, bridging::toJs(rt, std::forward<Args>(args), jsInvoker)...); 113 } else { 114 return bridging::fromJs<R>( 115 rt, 116 callback.call( 117 rt, bridging::toJs(rt, std::forward<Args>(args), jsInvoker)...), 118 jsInvoker); 119 } 120 } 121 122 private: 123 friend AsyncCallback<Args...>; 124 friend Bridging<SyncCallback>; 125 apply(std::tuple<Args...> && args)126 R apply(std::tuple<Args...> &&args) const { 127 return apply(std::move(args), std::index_sequence_for<Args...>{}); 128 } 129 130 template <size_t... Index> apply(std::tuple<Args...> && args,std::index_sequence<Index...>)131 R apply(std::tuple<Args...> &&args, std::index_sequence<Index...>) const { 132 return call(std::move(std::get<Index>(args))...); 133 } 134 135 // Held weakly so lifetime is managed by LongLivedObjectCollection. 136 std::weak_ptr<CallbackWrapper> wrapper_; 137 }; 138 139 template <typename... Args> 140 struct Bridging<AsyncCallback<Args...>> { 141 static AsyncCallback<Args...> fromJs( 142 jsi::Runtime &rt, 143 jsi::Function &&value, 144 const std::shared_ptr<CallInvoker> &jsInvoker) { 145 return AsyncCallback<Args...>(rt, std::move(value), jsInvoker); 146 } 147 148 static jsi::Function toJs( 149 jsi::Runtime &rt, 150 const AsyncCallback<Args...> &value) { 151 return value.callback_->function_.getFunction(rt); 152 } 153 }; 154 155 template <typename R, typename... Args> 156 struct Bridging<SyncCallback<R(Args...)>> { 157 static SyncCallback<R(Args...)> fromJs( 158 jsi::Runtime &rt, 159 jsi::Function &&value, 160 const std::shared_ptr<CallInvoker> &jsInvoker) { 161 return SyncCallback<R(Args...)>(rt, std::move(value), jsInvoker); 162 } 163 164 static jsi::Function toJs( 165 jsi::Runtime &rt, 166 const SyncCallback<R(Args...)> &value) { 167 return value.function_.getFunction(rt); 168 } 169 }; 170 171 template <typename R, typename... Args> 172 struct Bridging<butter::function<R(Args...)>> { 173 using Func = butter::function<R(Args...)>; 174 using IndexSequence = std::index_sequence_for<Args...>; 175 176 static constexpr size_t kArgumentCount = sizeof...(Args); 177 178 static jsi::Function toJs( 179 jsi::Runtime &rt, 180 Func fn, 181 const std::shared_ptr<CallInvoker> &jsInvoker) { 182 return jsi::Function::createFromHostFunction( 183 rt, 184 jsi::PropNameID::forAscii(rt, "BridgedFunction"), 185 kArgumentCount, 186 [fn = std::make_shared<Func>(std::move(fn)), jsInvoker]( 187 jsi::Runtime &rt, 188 const jsi::Value &, 189 const jsi::Value *args, 190 size_t count) -> jsi::Value { 191 if (count < kArgumentCount) { 192 throw jsi::JSError(rt, "Incorrect number of arguments"); 193 } 194 195 if constexpr (std::is_void_v<R>) { 196 callFromJs(*fn, rt, args, jsInvoker, IndexSequence{}); 197 return jsi::Value(); 198 } else { 199 return bridging::toJs( 200 rt, 201 callFromJs(*fn, rt, args, jsInvoker, IndexSequence{}), 202 jsInvoker); 203 } 204 }); 205 } 206 207 private: 208 template <size_t... Index> 209 static R callFromJs( 210 Func &fn, 211 jsi::Runtime &rt, 212 const jsi::Value *args, 213 const std::shared_ptr<CallInvoker> &jsInvoker, 214 std::index_sequence<Index...>) { 215 return fn(bridging::fromJs<Args>(rt, args[Index], jsInvoker)...); 216 } 217 }; 218 219 template <typename R, typename... Args> 220 struct Bridging< 221 std::function<R(Args...)>, 222 std::enable_if_t<!std::is_same_v< 223 std::function<R(Args...)>, 224 butter::function<R(Args...)>>>> 225 : Bridging<butter::function<R(Args...)>> {}; 226 227 template <typename R, typename... Args> 228 struct Bridging<R(Args...)> : Bridging<butter::function<R(Args...)>> {}; 229 230 template <typename R, typename... Args> 231 struct Bridging<R (*)(Args...)> : Bridging<butter::function<R(Args...)>> {}; 232 233 } // namespace ABI49_0_0facebook::ABI49_0_0React 234