diff --git a/ReactCommon/react/bridging/Bridging.h b/ReactCommon/react/bridging/Bridging.h index 340ad0dee56..b0ba75c55da 100644 --- a/ReactCommon/react/bridging/Bridging.h +++ b/ReactCommon/react/bridging/Bridging.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/ReactCommon/react/bridging/Class.h b/ReactCommon/react/bridging/Class.h new file mode 100644 index 00000000000..bb1afd00a8c --- /dev/null +++ b/ReactCommon/react/bridging/Class.h @@ -0,0 +1,55 @@ +/* + * 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 + +namespace facebook::react::bridging { + +template < + typename T, + typename C, + typename R, + typename... Args, + typename... JSArgs> +T callFromJs( + jsi::Runtime &rt, + R (C::*method)(jsi::Runtime &, Args...), + const std::shared_ptr &jsInvoker, + C *instance, + JSArgs &&...args) { + static_assert( + sizeof...(Args) == sizeof...(JSArgs), "Incorrect arguments length"); + + if constexpr (std::is_void_v) { + (instance->*method)( + rt, fromJs(rt, std::forward(args), jsInvoker)...); + + } else if constexpr (std::is_void_v) { + static_assert( + std::is_same_v, + "Void functions may only return undefined"); + + (instance->*method)( + rt, fromJs(rt, std::forward(args), jsInvoker)...); + return jsi::Value(); + + } else if constexpr (is_jsi_v) { + return toJs( + rt, + (instance->*method)( + rt, fromJs(rt, std::forward(args), jsInvoker)...), + jsInvoker); + + } else { + return (instance->*method)( + rt, fromJs(rt, std::forward(args), jsInvoker)...); + } +} + +} // namespace facebook::react::bridging diff --git a/ReactCommon/react/bridging/tests/ClassTest.cpp b/ReactCommon/react/bridging/tests/ClassTest.cpp new file mode 100644 index 00000000000..06c37820fab --- /dev/null +++ b/ReactCommon/react/bridging/tests/ClassTest.cpp @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#include "BridgingTest.h" + +namespace facebook::react { + +using namespace std::literals; + +struct TestClass { + TestClass(std::shared_ptr invoker) : invoker_(invoker) {} + + double add(jsi::Runtime &, int a, float b) { + return a + b; + } + + jsi::Object getObject(jsi::Runtime &, jsi::Object obj) { + return obj; + } + + AsyncPromise getPromise(jsi::Runtime &rt, std::string result) { + auto promise = AsyncPromise(rt, invoker_); + promise.resolve(result); + return promise; + } + + std::string + callFunc(jsi::Runtime &, SyncCallback func, int num) { + return func(num); + } + + void callAsync(jsi::Runtime &, AsyncCallback<> callback) { + callback(); + } + + private: + std::shared_ptr invoker_; +}; + +TEST_F(BridgingTest, callFromJsTest) { + auto instance = TestClass(invoker); + + EXPECT_EQ( + 3.0, + bridging::callFromJs( + rt, &TestClass::add, invoker, &instance, 1, 2.0)); + + auto object = jsi::Object(rt); + + EXPECT_TRUE(jsi::Object::strictEquals( + rt, + object, + bridging::callFromJs( + rt, &TestClass::getObject, invoker, &instance, object))); + + auto promise = bridging::callFromJs( + rt, + &TestClass::getPromise, + invoker, + &instance, + jsi::String::createFromAscii(rt, "hi")); + auto then = promise.getPropertyAsFunction(rt, "then"); + + std::string result; + then.callWithThis( + rt, + promise, + bridging::toJs( + rt, [&](std::string res) { result = res; }, invoker)); + + flushQueue(); + EXPECT_EQ("hi"s, result); + + auto func = function("(num) => String(num)"); + + EXPECT_EQ( + "1"s, + bridging::callFromJs( + rt, &TestClass::callFunc, invoker, &instance, func, 1) + .utf8(rt)); + + bool called = false; + func = bridging::toJs( + rt, [&] { called = true; }, invoker); + + bridging::callFromJs( + rt, &TestClass::callAsync, invoker, &instance, func); + + flushQueue(); + EXPECT_TRUE(called); +} + +} // namespace facebook::react