diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index e0910b2fa91..a9961cb2f4d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -116,6 +116,17 @@ public class ReactFeatureFlags { /** Feature Flag to control RN Android scrollEventThrottle prop. */ public static boolean enableScrollEventThrottle = false; + /** + * Feature flag that controls how turbo modules are exposed to JS + * + * + */ + public static int turboModuleBindingMode = 0; + /** * Feature Flag to enable View Recycling. When enabled, individual ViewManagers must still opt-in. */ diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp index 04238f8eb8f..a4d1c1289d9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/TurboModuleManager.cpp @@ -20,6 +20,17 @@ namespace facebook { namespace react { +constexpr static auto ReactFeatureFlagsJavaDescriptor = + "com/facebook/react/config/ReactFeatureFlags"; + +static int getFeatureFlagValue(const char *name) { + static const auto reactFeatureFlagsJavaDescriptor = + jni::findClassStatic(ReactFeatureFlagsJavaDescriptor); + const auto field = + reactFeatureFlagsJavaDescriptor->getStaticField(name); + return reactFeatureFlagsJavaDescriptor->getStaticFieldValue(field); +} + TurboModuleManager::TurboModuleManager( jni::alias_ref jThis, RuntimeExecutor runtimeExecutor, @@ -169,8 +180,13 @@ void TurboModuleManager::installJSIBindings() { return nullptr; }; + TurboModuleBindingMode bindingMode = static_cast( + getFeatureFlagValue("turboModuleBindingMode")); TurboModuleBinding::install( - runtime, std::move(turboModuleProvider), longLivedObjectCollection_); + runtime, + std::move(turboModuleProvider), + bindingMode, + longLivedObjectCollection_); }); } diff --git a/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp index b73fdfd2207..03c65ef2890 100644 --- a/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp +++ b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp @@ -24,13 +24,16 @@ namespace react { TurboModuleBinding::TurboModuleBinding( const TurboModuleProviderFunctionType &&moduleProvider, + TurboModuleBindingMode bindingMode, std::shared_ptr longLivedObjectCollection) : moduleProvider_(std::move(moduleProvider)), - longLivedObjectCollection_(std::move(longLivedObjectCollection)) {} + longLivedObjectCollection_(std::move(longLivedObjectCollection)), + bindingMode_(bindingMode) {} void TurboModuleBinding::install( jsi::Runtime &runtime, const TurboModuleProviderFunctionType &&moduleProvider, + TurboModuleBindingMode bindingMode, std::shared_ptr longLivedObjectCollection) { runtime.global().setProperty( runtime, @@ -41,6 +44,7 @@ void TurboModuleBinding::install( 1, [binding = TurboModuleBinding( std::move(moduleProvider), + bindingMode, std::move(longLivedObjectCollection))]( jsi::Runtime &rt, const jsi::Value &thisVal, @@ -76,15 +80,28 @@ jsi::Value TurboModuleBinding::getModule( module = moduleProvider_(moduleName); } if (module) { + // Default behaviour + if (bindingMode_ == TurboModuleBindingMode::HostObject) { + return jsi::Object::createFromHostObject(runtime, std::move(module)); + } + auto &jsRepresentation = module->jsRepresentation_; if (!jsRepresentation) { jsRepresentation = std::make_unique(runtime); - // Create plain object, with it's prototype mapped back to the hostobject. - // Any properties accessed are cached on the plain object. - auto hostObject = - jsi::Object::createFromHostObject(runtime, std::move(module)); - jsRepresentation->setProperty( - runtime, "__proto__", std::move(hostObject)); + if (bindingMode_ == TurboModuleBindingMode::Prototype) { + // Option 1: create plain object, with it's prototype mapped back to the + // hostobject. Any properties accessed are stored on the plain object + auto hostObject = + jsi::Object::createFromHostObject(runtime, std::move(module)); + jsRepresentation->setProperty( + runtime, "__proto__", std::move(hostObject)); + } else { + // Option 2: eagerly install all hostfunctions at this point, avoids + // prototype + for (auto &propName : module->getPropertyNames(runtime)) { + module->get(runtime, propName); + } + } } return jsi::Value(runtime, *jsRepresentation); } else { diff --git a/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h index 8d7742f3ff0..1791d4e272e 100644 --- a/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h +++ b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h @@ -18,6 +18,12 @@ namespace react { class JSCallInvoker; +enum class TurboModuleBindingMode : uint8_t { + HostObject = 0, + Prototype = 1, + Eager = 2, +}; + /** * Represents the JavaScript binding for the TurboModule system. */ @@ -30,11 +36,13 @@ class TurboModuleBinding { static void install( jsi::Runtime &runtime, const TurboModuleProviderFunctionType &&moduleProvider, + TurboModuleBindingMode bindingMode, std::shared_ptr longLivedObjectCollection); private: TurboModuleBinding( const TurboModuleProviderFunctionType &&moduleProvider, + TurboModuleBindingMode bindingMode, std::shared_ptr longLivedObjectCollection); virtual ~TurboModuleBinding(); @@ -50,6 +58,7 @@ class TurboModuleBinding { TurboModuleProviderFunctionType moduleProvider_; std::shared_ptr longLivedObjectCollection_; + TurboModuleBindingMode bindingMode_; }; } // namespace react diff --git a/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm index bf0bdb1a06e..dffe85b24c0 100644 --- a/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm +++ b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm @@ -777,12 +777,14 @@ static Class getFallbackClassFromName(const char *name) if (RCTGetTurboModuleCleanupMode() == kRCTGlobalScope || RCTGetTurboModuleCleanupMode() == kRCTGlobalScopeUsingRetainJSCallback) { runtimeExecutor([turboModuleProvider = std::move(turboModuleProvider)](jsi::Runtime &runtime) { - react::TurboModuleBinding::install(runtime, std::move(turboModuleProvider), nullptr); + react::TurboModuleBinding::install( + runtime, std::move(turboModuleProvider), TurboModuleBindingMode::HostObject, nullptr); }); } else if (RCTGetTurboModuleCleanupMode() == kRCTTurboModuleManagerScope) { runtimeExecutor([turboModuleProvider = std::move(turboModuleProvider), longLivedObjectCollection = _longLivedObjectCollection](jsi::Runtime &runtime) { - react::TurboModuleBinding::install(runtime, std::move(turboModuleProvider), longLivedObjectCollection); + react::TurboModuleBinding::install( + runtime, std::move(turboModuleProvider), TurboModuleBindingMode::HostObject, longLivedObjectCollection); }); } }