From 01d704dd459a05208153d41e512f8be6af924ca3 Mon Sep 17 00:00:00 2001 From: Phillip Pan Date: Mon, 19 Feb 2024 23:51:06 -0800 Subject: [PATCH] cxx module autolinking without codegen (#43054) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/43054 Changelog: [iOS][Android][Added] Experimental macro to autolink C++ turbomodules This implementation is inspired by RCT_EXPORT_MODULE on iOS. We keep a global data structure that maps module names to a lambda that returns the C++ turbomodule. This will come with a hit to startup time. the only way to avoid that is a solution that does static analysis of the list of C++ turbomodules being linked to the library. Reviewed By: fkgozali Differential Revision: D53602544 fbshipit-source-id: 8ea49fa576dc718f44b1595b68ab7c606c2db605 --- .../ReactCommon/TurboModuleManager.cpp | 9 +++ .../core/ReactCommon/CxxTurboModuleUtils.cpp | 32 +++++++++ .../core/ReactCommon/CxxTurboModuleUtils.h | 67 +++++++++++++++++++ .../ios/ReactCommon/RCTTurboModuleManager.mm | 9 +++ 4 files changed, 117 insertions(+) create mode 100644 packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.cpp create mode 100644 packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.h diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp index 85a2fdab53bfe2..cd9f033fe1d833 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -168,6 +169,14 @@ TurboModuleProviderFunctionType TurboModuleManager::createTurboModuleProvider( return cxxModule; } + auto& cxxTurboModuleMapProvider = globalExportedCxxTurboModuleMap(); + auto it = cxxTurboModuleMapProvider.find(name); + if (it != cxxTurboModuleMapProvider.end()) { + auto turboModule = it->second(jsCallInvoker); + turboModuleCache->insert({name, turboModule}); + return turboModule; + } + static auto getTurboLegacyCxxModule = javaPart->getClass() ->getMethod( diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.cpp new file mode 100644 index 00000000000000..4ddafa742addc9 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.cpp @@ -0,0 +1,32 @@ +/* + * 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 "CxxTurboModuleUtils.h" + +namespace facebook::react { + +std::unordered_map< + std::string, + std::function< + std::shared_ptr(std::shared_ptr jsInvoker)>>& +globalExportedCxxTurboModuleMap() { + static std::unordered_map< + std::string, + std::function( + std::shared_ptr jsInvoker)>> + map; + return map; +} + +void registerCxxModuleToGlobalModuleMap( + std::string name, + std::function( + std::shared_ptr jsInvoker)> moduleProviderFunc) { + globalExportedCxxTurboModuleMap()[name] = moduleProviderFunc; +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.h b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.h new file mode 100644 index 00000000000000..64c5cf3eb46fe2 --- /dev/null +++ b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/CxxTurboModuleUtils.h @@ -0,0 +1,67 @@ +/* + * 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 { + +std::unordered_map< + std::string, + std::function< + std::shared_ptr(std::shared_ptr jsInvoker)>>& +globalExportedCxxTurboModuleMap(); + +void registerCxxModuleToGlobalModuleMap( + std::string name, + std::function( + std::shared_ptr jsInvoker)> moduleProviderFunc); + +} // namespace facebook::react + +/* + * You can use this macro to register your C++ TurboModule in your .cpp + * implementation if you do not have access to getTurboModule:jsInvoker: + * callback. This will register the module before main() is called, + * so it will incur a startup cost. + * + * RCT_EXPORT_CXX_MODULE_EXPERIMENTAL(ModuleExample) becomes: + * + * #pragma clang diagnostic push + * #pragma clang diagnostic ignored "-Wglobal-constructors" + * struct ModuleExampleLoad { + * ModuleExampleLoad() { + * facebook::react::registerCxxModule(name, + * [&](std::shared_ptr jsInvoker) { + * return + * std::make_shared(jsInvoker); + * }); + * } + * }; + * static ModuleExampleLoad moduleExampleLoad; + * #pragma clang diagnostic pop + * + */ +#define RCT_EXPORT_CXX_MODULE_EXPERIMENTAL(name) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") struct \ + name##Load { \ + name##Load() { \ + facebook::react::registerCxxModuleToGlobalModuleMap( \ + #name, \ + [&](std::shared_ptr jsInvoker) { \ + return std::make_shared(jsInvoker); \ + }); \ + } \ + }; \ + static name##Load _##name##Load; \ + _Pragma("clang diagnostic pop") diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm index 09ea2122987fce..d93d606fce4117 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm @@ -27,6 +27,7 @@ #import #import #import +#import #import #import #import @@ -325,6 +326,14 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy TurboModulePerfLogger::moduleCreateFail(moduleName, moduleId); } + auto &cxxTurboModuleMapProvider = globalExportedCxxTurboModuleMap(); + auto it = cxxTurboModuleMapProvider.find(moduleName); + if (it != cxxTurboModuleMapProvider.end()) { + auto turboModule = it->second(_jsInvoker); + _turboModuleCache.insert({moduleName, turboModule}); + return turboModule; + } + /** * Step 2: Look for platform-specific modules. */