Skip to content

Commit

Permalink
cxx module autolinking without codegen
Browse files Browse the repository at this point in the history
Summary:
see https://fb.workplace.com/groups/2158787827656681/posts/2296646383870824/?comment_id=2303006146568181 for context

>hi everyone, update on how this directional development is going. currently, RuntimeExecutor appears to fulfill ExpoModules needs and has been integrated successfully on iOS. however, there is still a question on how to migrate callsites that require the runtime "on demand" when they are already on the JS thread. example from reanimated here (https://github.com/.../react.../blob/main/apple/REAModule.mm).

>Samuel and Pieter helped brainstorm a couple of options (exposing the raw runtime pointer, C++ turbomodules, deobfuscating the runtime pointer in the turbomodule invocation flow), but i felt like C++ turbomodule was the least hacky and had the most long-term prospects of these options.

>since the blocker is autolinking, i prototyped a macro similar to RCT_EXPORT_MODULE for cxx modules only and i would like to get everyone's comments: D53602544. Nicola is investigating android atm.

this implementation is inspired by RCT_EXPORT_MODULE, 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 codegen solution.

some other topics to figure out are:
- do we want this as a long term solution? probably not but expo is leery about codegen
- we should probably ban usage of this internally

Differential Revision: D53602544
  • Loading branch information
philIip authored and facebook-github-bot committed Feb 15, 2024
1 parent e4135e9 commit 6a2716a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<TurboModule>(std::shared_ptr<CallInvoker> jsInvoker)>>&
cxxTurboModuleMap() {
static std::unordered_map<
std::string,
std::function<std::shared_ptr<TurboModule>(
std::shared_ptr<CallInvoker> jsInvoker)>>
map;
return map;
}

void registerCxxModule(
std::string name,
std::function<std::shared_ptr<TurboModule>(
std::shared_ptr<CallInvoker> jsInvoker)> moduleProviderFunc) {
cxxTurboModuleMap()[name] = moduleProviderFunc;
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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 <cassert>
#include <string>
#include <unordered_map>

#include <jsi/jsi.h>

#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/CallbackWrapper.h>
#include <ReactCommon/TurboModule.h>

namespace facebook::react {

std::unordered_map<
std::string,
std::function<
std::shared_ptr<TurboModule>(std::shared_ptr<CallInvoker> jsInvoker)>>&
cxxTurboModuleMap();

void registerCxxModule(
std::string name,
std::function<std::shared_ptr<TurboModule>(
std::shared_ptr<CallInvoker> jsInvoker)> moduleProviderFunc);

} // namespace facebook::react

#define RCT_EXPORT_CXX_MODULE_EXPERIMENTAL(name) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") struct \
name##Load { \
name##Load() { \
facebook::react::registerCxxModule( \
#name, \
[&](std::shared_ptr<facebook::react::CallInvoker> jsInvoker) { \
return std::make_shared<facebook::react::name>(jsInvoker); \
}); \
} \
}; \
static name##Load _##name##Load; \
_Pragma("clang diagnostic pop")

// RCT_EXPORT_CXX_MODULE(NativeCxxModuleExample) turns into the following:
// #pragma clang diagnostic push
// #pragma clang diagnostic ignored "-Wglobal-constructors"
// struct NativeCxxModuleExampleLoad {
// NativeCxxModuleExampleLoad() {
// facebook::react::registerCxxModule(name,
// [&](std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
// return
// std::make_shared<facebook::react::NativeCxxModuleExample>(jsInvoker);
// });
// }
// };
// constexpr static NativeCxxModuleExampleLoad nativeCxxModuleExampleLoad;
// #pragma clang diagnostic pop
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#import <React/RCTPerformanceLogger.h>
#import <React/RCTRuntimeExecutorModule.h>
#import <React/RCTUtils.h>
#import <ReactCommon/CxxTurboModuleUtils.h>
#import <ReactCommon/TurboCxxModule.h>
#import <ReactCommon/TurboModulePerfLogger.h>
#import <ReactCommon/TurboModuleUtils.h>
Expand Down Expand Up @@ -325,6 +326,14 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
TurboModulePerfLogger::moduleCreateFail(moduleName, moduleId);
}

auto cxxTurboModuleMapProvider = cxxTurboModuleMap();
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.
*/
Expand Down

0 comments on commit 6a2716a

Please sign in to comment.