From 8d76670cd580bf3ce6015c7dde239c19e5a4e40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateo=20Guzm=C3=A1n?= Date: Mon, 9 Jun 2025 09:13:20 -0700 Subject: [PATCH] Migrate `ReactPackageTurboModuleManagerDelegate` to Kotlin (#51855) Summary: Migrate com.facebook.react.ReactPackageTurboModuleManagerDelegate to Kotlin. ## Changelog: [INTERNAL] - Migrate com.facebook.react.ReactPackageTurboModuleManagerDelegate to Kotlin Pull Request resolved: https://github.com/facebook/react-native/pull/51855 Test Plan: ```bash yarn test-android yarn android ``` Reviewed By: fabriziocucci Differential Revision: D76116517 Pulled By: cortinico fbshipit-source-id: 76dc4f7e16736a54be2bbfc1e2fa2e64b1433739 --- .../ReactAndroid/api/ReactAndroid.api | 6 +- .../com/facebook/react/LazyReactPackage.kt | 9 + ...eactPackageTurboModuleManagerDelegate.java | 269 ------------------ .../ReactPackageTurboModuleManagerDelegate.kt | 242 ++++++++++++++++ .../core/TurboModuleManagerDelegate.kt | 8 +- 5 files changed, 258 insertions(+), 276 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 2dffea184fd..e41d70d996b 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -368,10 +368,10 @@ public abstract class com/facebook/react/ReactPackageTurboModuleManagerDelegate public abstract class com/facebook/react/ReactPackageTurboModuleManagerDelegate$Builder { public fun ()V - public fun build ()Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate; + public final fun build ()Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate; protected abstract fun build (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/util/List;)Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate; - public fun setPackages (Ljava/util/List;)Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate$Builder; - public fun setReactApplicationContext (Lcom/facebook/react/bridge/ReactApplicationContext;)Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate$Builder; + public final fun setPackages (Ljava/util/List;)Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate$Builder; + public final fun setReactApplicationContext (Lcom/facebook/react/bridge/ReactApplicationContext;)Lcom/facebook/react/ReactPackageTurboModuleManagerDelegate$Builder; } public class com/facebook/react/ReactRootView : android/widget/FrameLayout, com/facebook/react/uimanager/ReactRoot, com/facebook/react/uimanager/RootView { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.kt index c9978cf9a28..ef7929b8ac7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.kt @@ -79,6 +79,15 @@ public abstract class LazyReactPackage : ReactPackage { */ protected abstract fun getNativeModules(reactContext: ReactApplicationContext): List + /** + * Internal accessor to [getNativeModules]. This is needed because [getNativeModules] was + * originally protected in Java (which had subclass + package visibility) and is now protected in + * Kotlin (which has only subclass visiblity). We add this accessor to prevent making + * [getNativeModules] public + */ + internal fun internal_getNativeModules(reactContext: ReactApplicationContext): List = + getNativeModules(reactContext) + /** * @param reactContext react application context that can be used to create modules * @return A [List]<[NativeModule]> to register diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java deleted file mode 100644 index 94d77633490..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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. - */ - -package com.facebook.react; - -import androidx.annotation.Nullable; -import com.facebook.infer.annotation.Assertions; -import com.facebook.jni.HybridData; -import com.facebook.react.bridge.CxxModuleWrapper; -import com.facebook.react.bridge.ModuleSpec; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags; -import com.facebook.react.internal.turbomodule.core.TurboModuleManagerDelegate; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.module.model.ReactModuleInfo; -import com.facebook.react.turbomodule.core.interfaces.TurboModule; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.inject.Provider; - -public abstract class ReactPackageTurboModuleManagerDelegate extends TurboModuleManagerDelegate { - interface ModuleProvider { - @Nullable - NativeModule getModule(String moduleName); - } - - private final List mModuleProviders = new ArrayList<>(); - private final Map> mPackageModuleInfos = - new HashMap<>(); - - private final boolean mShouldEnableLegacyModuleInterop = - ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture() - && ReactNativeNewArchitectureFeatureFlags.useTurboModuleInterop(); - - // Lazy Props - private List mPackages; - private ReactApplicationContext mReactContext; - - protected ReactPackageTurboModuleManagerDelegate( - ReactApplicationContext reactApplicationContext, List packages) { - super(); - initialize(reactApplicationContext, packages); - } - - protected ReactPackageTurboModuleManagerDelegate( - ReactApplicationContext reactApplicationContext, - List packages, - HybridData hybridData) { - super(hybridData); - initialize(reactApplicationContext, packages); - } - - private void initialize( - ReactApplicationContext reactApplicationContext, List packages) { - final ReactApplicationContext applicationContext = reactApplicationContext; - for (ReactPackage reactPackage : packages) { - if (reactPackage instanceof BaseReactPackage) { - final BaseReactPackage baseReactPackage = (BaseReactPackage) reactPackage; - final ModuleProvider moduleProvider = - moduleName -> baseReactPackage.getModule(moduleName, applicationContext); - mModuleProviders.add(moduleProvider); - mPackageModuleInfos.put( - moduleProvider, baseReactPackage.getReactModuleInfoProvider().getReactModuleInfos()); - continue; - } - - if (shouldSupportLegacyPackages() && reactPackage instanceof LazyReactPackage) { - // TODO(T145105887): Output warnings that LazyReactPackage was used - final LazyReactPackage lazyPkg = ((LazyReactPackage) reactPackage); - final List moduleSpecs = lazyPkg.getNativeModules(reactApplicationContext); - final Map> moduleSpecProviderMap = new HashMap<>(); - for (final ModuleSpec moduleSpec : moduleSpecs) { - moduleSpecProviderMap.put(moduleSpec.getName(), moduleSpec.getProvider()); - } - - final ModuleProvider moduleProvider = - moduleName -> { - Provider provider = moduleSpecProviderMap.get(moduleName); - return provider != null ? provider.get() : null; - }; - - mModuleProviders.add(moduleProvider); - mPackageModuleInfos.put( - moduleProvider, lazyPkg.getReactModuleInfoProvider().getReactModuleInfos()); - continue; - } - - if (shouldSupportLegacyPackages()) { - // TODO(T145105887): Output warnings that ReactPackage was used - final List nativeModules = - reactPackage.createNativeModules(reactApplicationContext); - - final Map moduleMap = new HashMap<>(); - final Map reactModuleInfoMap = new HashMap<>(); - - for (final NativeModule module : nativeModules) { - final Class moduleClass = module.getClass(); - final @Nullable ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class); - - final String moduleName = reactModule != null ? reactModule.name() : module.getName(); - - final ReactModuleInfo moduleInfo = - reactModule != null - ? new ReactModuleInfo( - moduleName, - moduleClass.getName(), - reactModule.canOverrideExistingModule(), - true, - reactModule.isCxxModule(), - ReactModuleInfo.classIsTurboModule(moduleClass)) - : new ReactModuleInfo( - moduleName, - moduleClass.getName(), - module.canOverrideExistingModule(), - true, - CxxModuleWrapper.class.isAssignableFrom(moduleClass), - ReactModuleInfo.classIsTurboModule(moduleClass)); - - reactModuleInfoMap.put(moduleName, moduleInfo); - moduleMap.put(moduleName, module); - } - - final ModuleProvider moduleProvider = moduleMap::get; - - mModuleProviders.add(moduleProvider); - mPackageModuleInfos.put(moduleProvider, reactModuleInfoMap); - } - } - } - - @Override - public boolean unstable_shouldEnableLegacyModuleInterop() { - return mShouldEnableLegacyModuleInterop; - } - - @Nullable - @Override - public TurboModule getModule(String moduleName) { - NativeModule resolvedModule = null; - - for (final ModuleProvider moduleProvider : mModuleProviders) { - final ReactModuleInfo moduleInfo = mPackageModuleInfos.get(moduleProvider).get(moduleName); - if (moduleInfo != null - && moduleInfo.isTurboModule() - && (resolvedModule == null || moduleInfo.canOverrideExistingModule())) { - - final NativeModule module = moduleProvider.getModule(moduleName); - if (module != null) { - resolvedModule = module; - } - } - } - - // Skip TurboModule-incompatible modules - boolean isLegacyModule = !(resolvedModule instanceof TurboModule); - if (isLegacyModule) { - return null; - } - - return (TurboModule) resolvedModule; - } - - @Override - public boolean unstable_isModuleRegistered(String moduleName) { - for (final ModuleProvider moduleProvider : mModuleProviders) { - final ReactModuleInfo moduleInfo = mPackageModuleInfos.get(moduleProvider).get(moduleName); - if (moduleInfo != null && moduleInfo.isTurboModule()) { - return true; - } - } - return false; - } - - @Override - public boolean unstable_isLegacyModuleRegistered(String moduleName) { - for (final ModuleProvider moduleProvider : mModuleProviders) { - final ReactModuleInfo moduleInfo = mPackageModuleInfos.get(moduleProvider).get(moduleName); - if (moduleInfo != null && !moduleInfo.isTurboModule()) { - return true; - } - } - return false; - } - - @Nullable - @Override - public NativeModule getLegacyModule(String moduleName) { - if (!unstable_shouldEnableLegacyModuleInterop()) { - return null; - } - - NativeModule resolvedModule = null; - - for (final ModuleProvider moduleProvider : mModuleProviders) { - final ReactModuleInfo moduleInfo = mPackageModuleInfos.get(moduleProvider).get(moduleName); - if (moduleInfo != null - && !moduleInfo.isTurboModule() - && (resolvedModule == null || moduleInfo.canOverrideExistingModule())) { - - final NativeModule module = moduleProvider.getModule(moduleName); - if (module != null) { - resolvedModule = module; - } - } - } - - // Skip TurboModule-compatible modules - boolean isLegacyModule = !(resolvedModule instanceof TurboModule); - if (!isLegacyModule) { - return null; - } - - return resolvedModule; - } - - @Override - public List getEagerInitModuleNames() { - List moduleNames = new ArrayList<>(); - for (final ModuleProvider moduleProvider : mModuleProviders) { - for (final ReactModuleInfo moduleInfo : mPackageModuleInfos.get(moduleProvider).values()) { - if (moduleInfo.isTurboModule() && moduleInfo.needsEagerInit()) { - moduleNames.add(moduleInfo.name()); - } - } - } - return moduleNames; - } - - private boolean shouldSupportLegacyPackages() { - return unstable_shouldEnableLegacyModuleInterop(); - } - - public abstract static class Builder { - private @Nullable List mPackages; - private @Nullable ReactApplicationContext mContext; - - public Builder setPackages(List packages) { - mPackages = new ArrayList<>(packages); - return this; - } - - public Builder setReactApplicationContext(ReactApplicationContext context) { - mContext = context; - return this; - } - - protected abstract ReactPackageTurboModuleManagerDelegate build( - ReactApplicationContext context, List packages); - - public ReactPackageTurboModuleManagerDelegate build() { - Assertions.assertNotNull( - mContext, - "The ReactApplicationContext must be provided to create" - + " ReactPackageTurboModuleManagerDelegate"); - Assertions.assertNotNull( - mPackages, - "A set of ReactPackages must be provided to create" - + " ReactPackageTurboModuleManagerDelegate"); - return build(mContext, mPackages); - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.kt new file mode 100644 index 00000000000..28e3bc0772b --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.kt @@ -0,0 +1,242 @@ +/* + * 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. + */ + +package com.facebook.react + +import com.facebook.jni.HybridData +import com.facebook.react.bridge.CxxModuleWrapper +import com.facebook.react.bridge.ModuleSpec +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags +import com.facebook.react.internal.turbomodule.core.TurboModuleManagerDelegate +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.turbomodule.core.interfaces.TurboModule +import javax.inject.Provider + +public abstract class ReactPackageTurboModuleManagerDelegate : TurboModuleManagerDelegate { + internal fun interface ModuleProvider { + fun getModule(moduleName: String): NativeModule? + } + + private val moduleProviders = mutableListOf() + private val packageModuleInfos = mutableMapOf>() + private val shouldEnableLegacyModuleInterop = + ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture() && + ReactNativeNewArchitectureFeatureFlags.useTurboModuleInterop() + + protected constructor( + reactApplicationContext: ReactApplicationContext, + packages: List + ) : super() { + initialize(reactApplicationContext, packages) + } + + protected constructor( + reactApplicationContext: ReactApplicationContext, + packages: List, + hybridData: HybridData + ) : super(hybridData) { + initialize(reactApplicationContext, packages) + } + + private fun initialize( + reactApplicationContext: ReactApplicationContext, + packages: List + ) { + val applicationContext: ReactApplicationContext = reactApplicationContext + for (reactPackage in packages) { + if (reactPackage is BaseReactPackage) { + val moduleProvider = ModuleProvider { moduleName: String -> + reactPackage.getModule(moduleName, applicationContext) + } + moduleProviders.add(moduleProvider) + packageModuleInfos[moduleProvider] = + reactPackage.getReactModuleInfoProvider().getReactModuleInfos() + continue + } + + @Suppress("DEPRECATION") + if (shouldSupportLegacyPackages() && reactPackage is LazyReactPackage) { + // TODO(T145105887): Output warnings that LazyReactPackage was used + val lazyPkg = reactPackage + val moduleSpecs: List = + lazyPkg.internal_getNativeModules(reactApplicationContext) + val moduleSpecProviderMap: MutableMap> = mutableMapOf() + for (moduleSpec in moduleSpecs) { + moduleSpecProviderMap[moduleSpec.getName()] = moduleSpec.getProvider() + } + + val moduleProvider = ModuleProvider { moduleName: String -> + moduleSpecProviderMap[moduleName]?.get() + } + + moduleProviders.add(moduleProvider) + packageModuleInfos[moduleProvider] = lazyPkg.reactModuleInfoProvider.getReactModuleInfos() + continue + } + + if (shouldSupportLegacyPackages()) { + // TODO(T145105887): Output warnings that ReactPackage was used + val nativeModules = reactPackage.createNativeModules(reactApplicationContext) + + val moduleMap: MutableMap = mutableMapOf() + val reactModuleInfoMap: MutableMap = mutableMapOf() + + for (module in nativeModules) { + val moduleClass: Class = module.javaClass + val reactModule = moduleClass.getAnnotation(ReactModule::class.java) + + val moduleName = reactModule?.name ?: module.name + + val moduleInfo: ReactModuleInfo = + if (reactModule != null) + ReactModuleInfo( + moduleName, + moduleClass.name, + reactModule.canOverrideExistingModule, + true, + reactModule.isCxxModule, + ReactModuleInfo.classIsTurboModule(moduleClass)) + else + ReactModuleInfo( + moduleName, + moduleClass.name, + module.canOverrideExistingModule(), + true, + CxxModuleWrapper::class.java.isAssignableFrom(moduleClass), + ReactModuleInfo.classIsTurboModule(moduleClass)) + + reactModuleInfoMap[moduleName] = moduleInfo + moduleMap[moduleName] = module + } + + val moduleProvider = ModuleProvider { module -> moduleMap[module] } + + moduleProviders.add(moduleProvider) + packageModuleInfos[moduleProvider] = reactModuleInfoMap + } + } + } + + override fun unstable_shouldEnableLegacyModuleInterop(): Boolean = shouldEnableLegacyModuleInterop + + override fun getModule(moduleName: String): TurboModule? { + var resolvedModule: NativeModule? = null + + for (moduleProvider in moduleProviders) { + val moduleInfo: ReactModuleInfo? = packageModuleInfos[moduleProvider]?.get(moduleName) + if (moduleInfo?.isTurboModule == true && + (resolvedModule == null || moduleInfo.canOverrideExistingModule)) { + val module = moduleProvider.getModule(moduleName) + if (module != null) { + resolvedModule = module + } + } + } + + // Skip TurboModule-incompatible modules + val isLegacyModule = resolvedModule !is TurboModule + if (isLegacyModule) { + return null + } + + return resolvedModule as TurboModule + } + + override fun unstable_isModuleRegistered(moduleName: String): Boolean { + for (moduleProvider in moduleProviders) { + val moduleInfo: ReactModuleInfo? = packageModuleInfos[moduleProvider]?.get(moduleName) + if (moduleInfo?.isTurboModule == true) { + return true + } + } + return false + } + + override fun unstable_isLegacyModuleRegistered(moduleName: String): Boolean { + for (moduleProvider in moduleProviders) { + val moduleInfo: ReactModuleInfo? = packageModuleInfos[moduleProvider]?.get(moduleName) + if (moduleInfo?.isTurboModule == false) { + return true + } + } + return false + } + + override fun getLegacyModule(moduleName: String): NativeModule? { + if (!unstable_shouldEnableLegacyModuleInterop()) { + return null + } + + var resolvedModule: NativeModule? = null + + for (moduleProvider in moduleProviders) { + val moduleInfo: ReactModuleInfo? = packageModuleInfos[moduleProvider]?.get(moduleName) + if (moduleInfo?.isTurboModule == false && + (resolvedModule == null || moduleInfo.canOverrideExistingModule)) { + val module = moduleProvider.getModule(moduleName) + if (module != null) { + resolvedModule = module + } + } + } + + // Skip TurboModule-compatible modules + val isLegacyModule = resolvedModule !is TurboModule + if (!isLegacyModule) { + return null + } + + return resolvedModule + } + + override fun getEagerInitModuleNames(): List = buildList { + for (moduleProvider in moduleProviders) { + for (moduleInfo in packageModuleInfos[moduleProvider]?.values ?: emptyList()) { + if (moduleInfo.isTurboModule && moduleInfo.needsEagerInit) { + add(moduleInfo.name) + } + } + } + } + + private fun shouldSupportLegacyPackages(): Boolean = unstable_shouldEnableLegacyModuleInterop() + + public abstract class Builder { + private var packages: List? = null + private var context: ReactApplicationContext? = null + + public fun setPackages(packages: List): Builder { + this.packages = packages.toList() + return this + } + + public fun setReactApplicationContext(context: ReactApplicationContext?): Builder { + this.context = context + return this + } + + protected abstract fun build( + context: ReactApplicationContext, + packages: List + ): ReactPackageTurboModuleManagerDelegate + + public fun build(): ReactPackageTurboModuleManagerDelegate { + val nonNullContext = + requireNotNull(context) { + "The ReactApplicationContext must be provided to create ReactPackageTurboModuleManagerDelegate" + } + val nonNullPackages = + requireNotNull(packages) { + "A set of ReactPackages must be provided to create ReactPackageTurboModuleManagerDelegate" + } + return build(nonNullContext, nonNullPackages) + } + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.kt index d9eb6ca616c..202e8aecb60 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.kt @@ -34,17 +34,17 @@ public abstract class TurboModuleManagerDelegate { * Create and return a TurboModule Java object with name `moduleName`. If `moduleName` isn't a * TurboModule, return null. */ - public abstract fun getModule(moduleName: String?): TurboModule? + public abstract fun getModule(moduleName: String): TurboModule? - public abstract fun unstable_isModuleRegistered(moduleName: String?): Boolean + public abstract fun unstable_isModuleRegistered(moduleName: String): Boolean /** * Create an return a legacy NativeModule with name `moduleName`. If `moduleName` is a * TurboModule, return null. */ - public open fun getLegacyModule(moduleName: String?): NativeModule? = null + public open fun getLegacyModule(moduleName: String): NativeModule? = null - public open fun unstable_isLegacyModuleRegistered(moduleName: String?): Boolean = false + public open fun unstable_isLegacyModuleRegistered(moduleName: String): Boolean = false public open fun getEagerInitModuleNames(): List = emptyList()