/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include #include namespace facebook::react { template class SyncCallback; template class AsyncCallback { public: AsyncCallback( jsi::Runtime &runtime, jsi::Function function, std::shared_ptr jsInvoker) : callback_(std::make_shared>( runtime, std::move(function), std::move(jsInvoker))) {} AsyncCallback(const AsyncCallback &) = default; AsyncCallback &operator=(const AsyncCallback &) = default; void operator()(Args... args) const { call(std::forward(args)...); } void call(Args... args) const { callInternal(std::nullopt, std::forward(args)...); } void callWithPriority(SchedulerPriority priority, Args... args) const { callInternal(priority, std::forward(args)...); } private: friend Bridging; std::shared_ptr> callback_; void callInternal(std::optional priority, Args... args) const { auto wrapper = callback_->wrapper_.lock(); if (!wrapper) { throw std::runtime_error("Failed to call invalidated async callback"); } auto fn = [callback = callback_, argsPtr = std::make_shared>( std::make_tuple(std::forward(args)...))] { callback->apply(std::move(*argsPtr)); }; if (priority) { wrapper->jsInvoker().invokeAsync(*priority, std::move(fn)); } else { wrapper->jsInvoker().invokeAsync(std::move(fn)); } } }; template class SyncCallback { public: SyncCallback( jsi::Runtime &rt, jsi::Function function, std::shared_ptr jsInvoker) : wrapper_(CallbackWrapper::createWeak( std::move(function), rt, std::move(jsInvoker))) {} // Disallow moving to prevent function from get called on another thread. SyncCallback(SyncCallback &&) = delete; SyncCallback &operator=(SyncCallback &&) = delete; ~SyncCallback() { if (auto wrapper = wrapper_.lock()) { wrapper->destroy(); } } R operator()(Args... args) const { return call(std::forward(args)...); } R call(Args... args) const { auto wrapper = wrapper_.lock(); if (!wrapper) { throw std::runtime_error("Failed to call invalidated sync callback"); } auto &callback = wrapper->callback(); auto &rt = wrapper->runtime(); auto jsInvoker = wrapper->jsInvokerPtr(); if constexpr (std::is_void_v) { callback.call( rt, bridging::toJs(rt, std::forward(args), jsInvoker)...); } else { return bridging::fromJs( rt, callback.call( rt, bridging::toJs(rt, std::forward(args), jsInvoker)...), jsInvoker); } } private: friend AsyncCallback; friend Bridging; R apply(std::tuple &&args) const { return apply(std::move(args), std::index_sequence_for{}); } template R apply(std::tuple &&args, std::index_sequence) const { return call(std::move(std::get(args))...); } // Held weakly so lifetime is managed by LongLivedObjectCollection. std::weak_ptr wrapper_; }; template struct Bridging> { static AsyncCallback fromJs( jsi::Runtime &rt, jsi::Function &&value, const std::shared_ptr &jsInvoker) { return AsyncCallback(rt, std::move(value), jsInvoker); } static jsi::Function toJs( jsi::Runtime &rt, const AsyncCallback &value) { return value.callback_->function_.getFunction(rt); } }; template struct Bridging> { static SyncCallback fromJs( jsi::Runtime &rt, jsi::Function &&value, const std::shared_ptr &jsInvoker) { return SyncCallback(rt, std::move(value), jsInvoker); } static jsi::Function toJs( jsi::Runtime &rt, const SyncCallback &value) { return value.function_.getFunction(rt); } }; template struct Bridging> { using Func = butter::function; using IndexSequence = std::index_sequence_for; static constexpr size_t kArgumentCount = sizeof...(Args); static jsi::Function toJs( jsi::Runtime &rt, Func fn, const std::shared_ptr &jsInvoker) { return jsi::Function::createFromHostFunction( rt, jsi::PropNameID::forAscii(rt, "BridgedFunction"), kArgumentCount, [fn = std::make_shared(std::move(fn)), jsInvoker]( jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value { if (count < kArgumentCount) { throw jsi::JSError(rt, "Incorrect number of arguments"); } if constexpr (std::is_void_v) { callFromJs(*fn, rt, args, jsInvoker, IndexSequence{}); return jsi::Value(); } else { return bridging::toJs( rt, callFromJs(*fn, rt, args, jsInvoker, IndexSequence{}), jsInvoker); } }); } private: template static R callFromJs( Func &fn, jsi::Runtime &rt, const jsi::Value *args, const std::shared_ptr &jsInvoker, std::index_sequence) { return fn(bridging::fromJs(rt, args[Index], jsInvoker)...); } }; template struct Bridging< std::function, std::enable_if_t, butter::function>>> : Bridging> {}; template struct Bridging : Bridging> {}; template struct Bridging : Bridging> {}; } // namespace facebook::react