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