Make NativeModules.foo also load turbo modules (#47598)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47598

## Changes
Now:
- in bridgeless, NativeModules.foo will also return turbo modules. And, global.__turboModuleProxy no longer exists.
- in bridge, nothing changes.

| **JS API**                               | **Bridge**             | ***[Before]* Bridgeless**   | ***[Before]* Bridgeless w/ Interop**| ***[After]* Bridgeless**
| global.__turboModuleProxy |  turbo modules      | turbo modules                     | turbo modules                                     |**deleted**
| global.nativeModuleProxy    |  legacy  modules   | error                                      | legacy modules                                  | turbo + legacy modules

## Justification
This reduces the cost for adopting the new architecture:
- Prior, you had to migrate the module itself, **and** all its callsites: NativeModules.foo -> NativeFoo
- Now, you have to migrate the module itself **only**.

This simplifies the interop layer logic in bridgeless: all modules come from the same thing.

Changelog: [General][Breaking] Bridgeless: Make NativeModules.foo load turbomodules (unset turboModuleProxy in bridgeless).

Reviewed By: javache

Differential Revision: D65896934

fbshipit-source-id: 10883c292b78759fceac5bd984e0cdf8a679fc67
This commit is contained in:
Ramanpreet Nara 2024-11-18 17:22:51 -08:00 committed by Facebook GitHub Bot
parent d1b0e9a30b
commit cc5f17d5a2
2 changed files with 70 additions and 49 deletions

View File

@ -16,9 +16,6 @@ const NativeModules = require('../BatchedBridge/NativeModules');
const turboModuleProxy = global.__turboModuleProxy;
const useLegacyNativeModuleInterop =
global.RN$Bridgeless !== true || global.RN$TurboInterop === true;
function requireModule<T: TurboModule>(name: string): ?T {
if (turboModuleProxy != null) {
const module: ?T = turboModuleProxy(name);
@ -27,8 +24,11 @@ function requireModule<T: TurboModule>(name: string): ?T {
}
}
if (useLegacyNativeModuleInterop) {
// Backward compatibility layer during migration.
if (
global.RN$Bridgeless !== true ||
global.RN$TurboInterop === true ||
global.RN$UnifiedNativeModuleProxy === true
) {
const legacyModule: ?T = NativeModules[name];
if (legacyModule != null) {
return legacyModule;

View File

@ -17,11 +17,25 @@ using namespace facebook;
namespace facebook::react {
class BridgelessNativeModuleProxy : public jsi::HostObject {
std::unique_ptr<TurboModuleBinding> binding_;
TurboModuleBinding turboBinding_;
std::unique_ptr<TurboModuleBinding> legacyBinding_;
public:
BridgelessNativeModuleProxy(std::unique_ptr<TurboModuleBinding> binding)
: binding_(std::move(binding)) {}
BridgelessNativeModuleProxy(
jsi::Runtime& runtime,
TurboModuleProviderFunctionType&& moduleProvider,
TurboModuleProviderFunctionType&& legacyModuleProvider,
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection)
: turboBinding_(
runtime,
std::move(moduleProvider),
longLivedObjectCollection),
legacyBinding_(
legacyModuleProvider ? std::make_unique<TurboModuleBinding>(
runtime,
std::move(legacyModuleProvider),
longLivedObjectCollection)
: nullptr) {}
jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override {
/**
@ -42,14 +56,19 @@ class BridgelessNativeModuleProxy : public jsi::HostObject {
return jsi::Value(false);
}
if (binding_) {
return binding_->getModule(runtime, moduleName);
auto turboModule = turboBinding_.getModule(runtime, moduleName);
if (turboModule.isObject()) {
return turboModule;
}
throw jsi::JSError(
runtime,
"Tried to access NativeModule \"" + name.utf8(runtime) +
"\" from the bridge. This isn't allowed in Bridgeless mode.");
if (legacyBinding_) {
auto legacyModule = legacyBinding_->getModule(runtime, moduleName);
if (legacyModule.isObject()) {
return legacyModule;
}
}
return jsi::Value::null();
}
void set(
@ -79,44 +98,46 @@ void TurboModuleBinding::install(
TurboModuleProviderFunctionType&& moduleProvider,
TurboModuleProviderFunctionType&& legacyModuleProvider,
std::shared_ptr<LongLivedObjectCollection> longLivedObjectCollection) {
runtime.global().setProperty(
runtime,
"__turboModuleProxy",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"),
1,
[binding = TurboModuleBinding(
runtime, std::move(moduleProvider), longLivedObjectCollection)](
jsi::Runtime& rt,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) {
if (count < 1) {
throw std::invalid_argument(
"__turboModuleProxy must be called with at least 1 argument");
}
std::string moduleName = args[0].getString(rt).utf8(rt);
return binding.getModule(rt, moduleName);
}));
// TODO(T208105802): We can get this information from the native side!
auto isBridgeless = runtime.global().hasProperty(runtime, "RN$Bridgeless");
if (runtime.global().hasProperty(runtime, "RN$Bridgeless")) {
bool rnTurboInterop = legacyModuleProvider != nullptr;
auto turboModuleBinding = legacyModuleProvider
? std::make_unique<TurboModuleBinding>(
runtime,
std::move(legacyModuleProvider),
longLivedObjectCollection)
: nullptr;
auto nativeModuleProxy = std::make_shared<BridgelessNativeModuleProxy>(
std::move(turboModuleBinding));
defineReadOnlyGlobal(
runtime, "RN$TurboInterop", jsi::Value(rnTurboInterop));
defineReadOnlyGlobal(
if (!isBridgeless) {
runtime.global().setProperty(
runtime,
"nativeModuleProxy",
jsi::Object::createFromHostObject(runtime, nativeModuleProxy));
"__turboModuleProxy",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__turboModuleProxy"),
1,
[binding = TurboModuleBinding(
runtime,
std::move(moduleProvider),
longLivedObjectCollection)](
jsi::Runtime& rt,
const jsi::Value& /*thisVal*/,
const jsi::Value* args,
size_t count) {
if (count < 1) {
throw std::invalid_argument(
"__turboModuleProxy must be called with at least 1 argument");
}
std::string moduleName = args[0].getString(rt).utf8(rt);
return binding.getModule(rt, moduleName);
}));
return;
}
defineReadOnlyGlobal(runtime, "RN$UnifiedNativeModuleProxy", true);
defineReadOnlyGlobal(
runtime,
"nativeModuleProxy",
jsi::Object::createFromHostObject(
runtime,
std::make_shared<BridgelessNativeModuleProxy>(
runtime,
std::move(moduleProvider),
std::move(legacyModuleProvider),
longLivedObjectCollection)));
}
TurboModuleBinding::~TurboModuleBinding() {