Do not attempt to call Class.forName in OSS (#43816)

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

We should not attempt to load generated classes by the Annotation processor in OSS because we simply don't run it, so those classes will fail to load. I'm short-circuiting the logic here.

This is sustainability work as we got a report for this in OSS a while ago and I never got the time to work on it.

Changelog:
[Internal] [Changed] - Do not attempt to call Class.forName in OSS

Reviewed By: rshest

Differential Revision: D55693479

fbshipit-source-id: 3ec84e2c7940011b48f354058b5099b46065166d
This commit is contained in:
Nicola Corti
2024-04-03 23:00:03 -07:00
committed by Facebook GitHub Bot
parent 8c62df6396
commit 26cc8cddf9
6 changed files with 201 additions and 130 deletions
@@ -1666,6 +1666,12 @@ public class com/facebook/react/bridge/queue/ReactQueueConfigurationSpec$Builder
public fun setNativeModulesQueueThreadSpec (Lcom/facebook/react/bridge/queue/MessageQueueThreadSpec;)Lcom/facebook/react/bridge/queue/ReactQueueConfigurationSpec$Builder;
}
public final class com/facebook/react/common/ClassFinder {
public static final field INSTANCE Lcom/facebook/react/common/ClassFinder;
public static final fun canLoadClassesFromAnnotationProcessors ()Z
public static final fun findClass (Ljava/lang/String;)Ljava/lang/Class;
}
public class com/facebook/react/common/ClearableSynchronizedPool : androidx/core/util/Pools$Pool {
public fun <init> (I)V
public fun acquire ()Ljava/lang/Object;
@@ -16,6 +16,7 @@ import androidx.annotation.Nullable;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.common.ClassFinder;
import com.facebook.react.devsupport.LogBoxModule;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
@@ -76,48 +77,21 @@ class CoreModulesPackage extends TurboReactPackage implements ReactPackageLogger
/**
* This method is overridden, since OSS does not run the annotation processor to generate {@link
* CoreModulesPackage$$ReactModuleInfoProvider} class. Here we check if it exists. If it does not
* exist, we generate one manually in {@link
* CoreModulesPackage#getReactModuleInfoByInitialization()} and return that instead.
* CoreModulesPackage$$ReactModuleInfoProvider} class. Here we check if it exists with the method
* {@link canLoadClassesFromAnnotationProcessors}. If it does not exist, we generate one manually
* in {@link CoreModulesPackage#getReactModuleInfoByInitialization()} and return that instead.
*/
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
if (!ClassFinder.canLoadClassesFromAnnotationProcessors()) {
return fallbackForMissingClass();
}
try {
Class<?> reactModuleInfoProviderClass =
Class.forName("com.facebook.react.CoreModulesPackage$$ReactModuleInfoProvider");
ClassFinder.findClass("com.facebook.react.CoreModulesPackage$$ReactModuleInfoProvider");
return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance();
} catch (ClassNotFoundException e) {
// In OSS case, the annotation processor does not run. We fall back on creating this byhand
Class<? extends NativeModule>[] moduleList =
new Class[] {
AndroidInfoModule.class,
DeviceEventManagerModule.class,
DeviceInfoModule.class,
DevSettingsModule.class,
ExceptionsManagerModule.class,
LogBoxModule.class,
HeadlessJsTaskSupportModule.class,
SourceCodeModule.class,
TimingModule.class,
UIManagerModule.class,
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
return () -> reactModuleInfoMap;
return fallbackForMissingClass();
} catch (InstantiationException e) {
throw new RuntimeException(
"No ReactModuleInfoProvider for CoreModulesPackage$$ReactModuleInfoProvider", e);
@@ -127,6 +101,40 @@ class CoreModulesPackage extends TurboReactPackage implements ReactPackageLogger
}
}
private ReactModuleInfoProvider fallbackForMissingClass() {
// In OSS case, the annotation processor does not run. We fall back on creating this byhand
Class<? extends NativeModule>[] moduleList =
new Class[] {
AndroidInfoModule.class,
DeviceEventManagerModule.class,
DeviceInfoModule.class,
DevSettingsModule.class,
ExceptionsManagerModule.class,
LogBoxModule.class,
HeadlessJsTaskSupportModule.class,
SourceCodeModule.class,
TimingModule.class,
UIManagerModule.class,
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
return () -> reactModuleInfoMap;
}
@Override
public NativeModule getModule(String name, ReactApplicationContext reactContext) {
switch (name) {
@@ -11,6 +11,7 @@ import androidx.annotation.Nullable;
import com.facebook.react.bridge.ModuleSpec;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.ClassFinder;
import com.facebook.react.devsupport.JSCHeapCapture;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
@@ -53,30 +54,15 @@ public class DebugCorePackage extends TurboReactPackage implements ViewManagerOn
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
if (!ClassFinder.canLoadClassesFromAnnotationProcessors()) {
return fallbackForMissingClass();
}
try {
Class<?> reactModuleInfoProviderClass =
Class.forName("com.facebook.react.DebugCorePackage$$ReactModuleInfoProvider");
ClassFinder.findClass("com.facebook.react.DebugCorePackage$$ReactModuleInfoProvider");
return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance();
} catch (ClassNotFoundException e) {
// In OSS case, the annotation processor does not run. We fall back on creating this by hand
Class<? extends NativeModule>[] moduleList = new Class[] {JSCHeapCapture.class};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
return () -> reactModuleInfoMap;
return fallbackForMissingClass();
} catch (InstantiationException e) {
throw new RuntimeException(
"No ReactModuleInfoProvider for DebugCorePackage$$ReactModuleInfoProvider", e);
@@ -86,6 +72,28 @@ public class DebugCorePackage extends TurboReactPackage implements ViewManagerOn
}
}
private ReactModuleInfoProvider fallbackForMissingClass() {
// In OSS case, the annotation processor does not run. We fall back on creating this by hand
Class<? extends NativeModule>[] moduleList = new Class[] {JSCHeapCapture.class};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
return () -> reactModuleInfoMap;
}
private static void appendMap(
Map<String, ModuleSpec> map, String name, Provider<? extends NativeModule> provider) {
map.put(name, ModuleSpec.viewManagerSpec(provider));
@@ -0,0 +1,30 @@
/*
* 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.common
import com.facebook.react.BuildConfig
import kotlin.jvm.Throws
public object ClassFinder {
/**
* We don't run the ModuleInfoProvider Annotation Processor in OSS, so there is no need to attempt
* to call Class.forName() as we know for sure that those classes won't be there.
*/
@JvmStatic
public fun canLoadClassesFromAnnotationProcessors(): Boolean = BuildConfig.IS_INTERNAL_BUILD
@JvmStatic
@Throws(ClassNotFoundException::class)
public fun findClass(className: String): Class<*>? {
if (canLoadClassesFromAnnotationProcessors().not()) {
return null
}
return Class.forName(className)
}
}
@@ -8,10 +8,12 @@
package com.facebook.react.runtime;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.ClassFinder;
import com.facebook.react.devsupport.LogBoxModule;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.module.annotations.ReactModule;
@@ -74,38 +76,16 @@ class CoreReactPackage extends TurboReactPackage {
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
if (!ClassFinder.canLoadClassesFromAnnotationProcessors()) {
return fallbackForMissingClass();
}
try {
Class<?> reactModuleInfoProviderClass =
Class.forName(CoreReactPackage.class.getName() + "$$ReactModuleInfoProvider");
return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance();
ClassFinder.findClass(CoreReactPackage.class.getName() + "$$ReactModuleInfoProvider");
return (ReactModuleInfoProvider)
Assertions.assertNotNull(reactModuleInfoProviderClass).newInstance();
} catch (ClassNotFoundException e) {
// In OSS case, the annotation processor does not run. We fall back on creating this byhand
Class<? extends NativeModule>[] moduleList =
new Class[] {
AndroidInfoModule.class,
DeviceInfoModule.class,
SourceCodeModule.class,
DevSettingsModule.class,
DeviceEventManagerModule.class,
LogBoxModule.class,
ExceptionsManagerModule.class,
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
if (reactModule != null) {
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
}
return () -> reactModuleInfoMap;
return fallbackForMissingClass();
} catch (InstantiationException e) {
throw new RuntimeException(
"No ReactModuleInfoProvider for "
@@ -120,4 +100,34 @@ class CoreReactPackage extends TurboReactPackage {
e);
}
}
private ReactModuleInfoProvider fallbackForMissingClass() {
// In OSS case, the annotation processor does not run. We fall back on creating this byhand
Class<? extends NativeModule>[] moduleList =
new Class[] {
AndroidInfoModule.class,
DeviceInfoModule.class,
SourceCodeModule.class,
DevSettingsModule.class,
DeviceEventManagerModule.class,
LogBoxModule.class,
ExceptionsManagerModule.class,
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
if (reactModule != null) {
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
}
return () -> reactModuleInfoMap;
}
}
@@ -15,6 +15,7 @@ import com.facebook.react.animated.NativeAnimatedModule;
import com.facebook.react.bridge.ModuleSpec;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.ClassFinder;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
import com.facebook.react.module.model.ReactModuleInfo;
@@ -252,56 +253,16 @@ public class MainReactPackage extends TurboReactPackage implements ViewManagerOn
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
if (!ClassFinder.canLoadClassesFromAnnotationProcessors()) {
return fallbackForMissingClass();
}
try {
Class<?> reactModuleInfoProviderClass =
Class.forName("com.facebook.react.shell.MainReactPackage$$ReactModuleInfoProvider");
ClassFinder.findClass(
"com.facebook.react.shell.MainReactPackage$$ReactModuleInfoProvider");
return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance();
} catch (ClassNotFoundException e) {
// In the OSS case, the annotation processor does not run. We fall back to creating this by
// hand
Class<? extends NativeModule>[] moduleList =
new Class[] {
AccessibilityInfoModule.class,
AppearanceModule.class,
AppStateModule.class,
BlobModule.class,
DevLoadingModule.class,
FileReaderModule.class,
ClipboardModule.class,
DialogModule.class,
FrescoModule.class,
I18nManagerModule.class,
ImageLoaderModule.class,
ImageStoreManager.class,
IntentModule.class,
NativeAnimatedModule.class,
NetworkingModule.class,
PermissionsModule.class,
DevToolsSettingsManagerModule.class,
ShareModule.class,
StatusBarModule.class,
SoundManagerModule.class,
ToastModule.class,
VibrationModule.class,
WebSocketModule.class
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
if (reactModule != null) {
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
}
return () -> reactModuleInfoMap;
return fallbackForMissingClass();
} catch (InstantiationException e) {
throw new RuntimeException(
"No ReactModuleInfoProvider for"
@@ -314,4 +275,52 @@ public class MainReactPackage extends TurboReactPackage implements ViewManagerOn
e);
}
}
private ReactModuleInfoProvider fallbackForMissingClass() {
// In the OSS case, the annotation processor does not run.
// We fall back to creating this by hand
Class<? extends NativeModule>[] moduleList =
new Class[] {
AccessibilityInfoModule.class,
AppearanceModule.class,
AppStateModule.class,
BlobModule.class,
DevLoadingModule.class,
FileReaderModule.class,
ClipboardModule.class,
DialogModule.class,
FrescoModule.class,
I18nManagerModule.class,
ImageLoaderModule.class,
ImageStoreManager.class,
IntentModule.class,
NativeAnimatedModule.class,
NetworkingModule.class,
PermissionsModule.class,
DevToolsSettingsManagerModule.class,
ShareModule.class,
StatusBarModule.class,
SoundManagerModule.class,
ToastModule.class,
VibrationModule.class,
WebSocketModule.class
};
final Map<String, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
for (Class<? extends NativeModule> moduleClass : moduleList) {
ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
if (reactModule != null) {
reactModuleInfoMap.put(
reactModule.name(),
new ReactModuleInfo(
reactModule.name(),
moduleClass.getName(),
reactModule.canOverrideExistingModule(),
reactModule.needsEagerInit(),
reactModule.isCxxModule(),
ReactModuleInfo.classIsTurboModule(moduleClass)));
}
}
return () -> reactModuleInfoMap;
}
}