From 2097278d2a91218274cb661ebf37cc8dbb78bf25 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 27 Oct 2022 04:47:25 -0700 Subject: [PATCH] Update the template to load the correct JS engine at runtime. (#35095) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/35095 This change will make sure that we load the correct JS engine at runtime, by using the BuildConfig flag that RNGP sets for us. This will solve a lot of noise in adb logcat for users seeing stacktraces mentioning failing to load `jscexecutor` library. This is also a breaking change, but as the API was not widely used nor advertised in the template, we should be fine by just mentioning this in the release notes. Changelog: [Android] [Changed] - Update the template to load the correct JS engine at runtime Reviewed By: cipolleschi Differential Revision: D40710597 fbshipit-source-id: d59a7a52b22a9bf273ea89094c6620c3ecf6eb00 --- .../react/JSEngineResolutionAlgorithm.java | 18 +++++++++ .../com/facebook/react/JSInterpreter.java | 18 --------- .../react/ReactInstanceManagerBuilder.java | 39 +++++++------------ .../com/facebook/react/ReactNativeHost.java | 8 ++++ .../react/defaults/DefaultReactNativeHost.kt | 24 +++++++++++- packages/rn-tester/android/app/build.gradle | 2 + .../react/uiapp/RNTesterApplication.java | 5 +++ .../java/com/helloworld/MainApplication.java | 5 +++ 8 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/JSEngineResolutionAlgorithm.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/JSEngineResolutionAlgorithm.java b/ReactAndroid/src/main/java/com/facebook/react/JSEngineResolutionAlgorithm.java new file mode 100644 index 00000000000000..7f33bc78c5a55c --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/JSEngineResolutionAlgorithm.java @@ -0,0 +1,18 @@ +/* + * 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; + +/** + * An enum that specifies the algorithm to use when loading theJS Engine. {@link #JSC} will load + * JavaScriptCore first and fail if it is not available. {@link #HERMES} will load Hermes first and + * fail if it is not available. + */ +public enum JSEngineResolutionAlgorithm { + JSC, + HERMES +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java b/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java deleted file mode 100644 index 7e960263cc55da..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java +++ /dev/null @@ -1,18 +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; - -/** - * An enum that specifies the JS Engine to be used in the app Old Logic uses the legacy code - * JSC/HERMES loads the respective engine using the revamped logic - */ -public enum JSInterpreter { - OLD_LOGIC, - JSC, - HERMES -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index 6dcb191808611f..68ac1ed63451e1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -14,6 +14,7 @@ import android.app.Application; import android.content.Context; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.hermes.reactexecutor.HermesExecutor; import com.facebook.hermes.reactexecutor.HermesExecutorFactory; import com.facebook.infer.annotation.Assertions; @@ -40,6 +41,8 @@ /** Builder class for {@link ReactInstanceManager} */ public class ReactInstanceManagerBuilder { + private static final String TAG = ReactInstanceManagerBuilder.class.getSimpleName(); + private final List mPackages = new ArrayList<>(); private @Nullable String mJSBundleAssetUrl; @@ -64,7 +67,7 @@ public class ReactInstanceManagerBuilder { private @Nullable Map mCustomPackagerCommandHandlers; private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; - private JSInterpreter jsInterpreter = JSInterpreter.OLD_LOGIC; + private JSEngineResolutionAlgorithm jsEngineResolutionAlgorithm = null; /* package protected */ ReactInstanceManagerBuilder() {} @@ -118,28 +121,12 @@ public ReactInstanceManagerBuilder setJSBundleLoader(JSBundleLoader jsBundleLoad } /** - * Sets the jsEngine as JSC or HERMES as per the setJsEngineAsHermes call Uses the enum {@link - * JSInterpreter} - * - * @param jsInterpreter - */ - private void setJSEngine(JSInterpreter jsInterpreter) { - this.jsInterpreter = jsInterpreter; - } - - /** - * Utility setter to set the required JSEngine as HERMES or JSC Defaults to OLD_LOGIC if not - * called by the host app - * - * @param hermesEnabled hermesEnabled = true sets the JS Engine as HERMES and JSC otherwise + * Sets the JS Engine to load as either Hermes or JSC. If not set, the default is JSC with a + * Hermes fallback. */ - public ReactInstanceManagerBuilder setJsEngineAsHermes(boolean hermesEnabled) { - if (hermesEnabled) { - setJSEngine(JSInterpreter.HERMES); - } else { - setJSEngine(JSInterpreter.JSC); - } - return this; + private void setJSEngineResolutionAlgorithm( + @Nullable JSEngineResolutionAlgorithm jsEngineResolutionAlgorithm) { + this.jsEngineResolutionAlgorithm = jsEngineResolutionAlgorithm; } /** @@ -365,7 +352,11 @@ private JavaScriptExecutorFactory getDefaultJSExecutorFactory( // if nothing is specified, use old loading method // else load the required engine - if (jsInterpreter == JSInterpreter.OLD_LOGIC) { + if (jsEngineResolutionAlgorithm == null) { + FLog.w( + TAG, + "You're not setting the JS Engine Resolution Algorithm. " + + "We'll try to load JSC first, and if it fails we'll fallback to Hermes"); try { // If JSC is included, use it as normal initializeSoLoaderIfNecessary(applicationContext); @@ -378,7 +369,7 @@ private JavaScriptExecutorFactory getDefaultJSExecutorFactory( HermesExecutor.loadLibrary(); return new HermesExecutorFactory(); } - } else if (jsInterpreter == JSInterpreter.HERMES) { + } else if (jsEngineResolutionAlgorithm == JSEngineResolutionAlgorithm.HERMES) { HermesExecutor.loadLibrary(); return new HermesExecutorFactory(); } else { diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java index 02a23c36a10bb1..d030019b035373 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java @@ -190,4 +190,12 @@ protected String getJSMainModuleName() { * default ones, you'll want to include more packages here. */ protected abstract List getPackages(); + + /** + * Returns the {@link JSEngineResolutionAlgorithm} to be used when loading the JS engine. If null, + * will try to load JSC first and fallback to Hermes if JSC is not available. + */ + protected @Nullable JSEngineResolutionAlgorithm getJSEngineResolutionAlgorithm() { + return null; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt index 9bbaee4ebbfadb..23c0bada20a220 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt +++ b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt @@ -8,6 +8,7 @@ package com.facebook.react.defaults import android.app.Application +import com.facebook.react.JSEngineResolutionAlgorithm import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackageTurboModuleManagerDelegate import com.facebook.react.bridge.JSIModulePackage @@ -20,8 +21,10 @@ import com.facebook.react.bridge.JSIModulePackage * providing the default TurboModuleManagerDelegateBuilder and the default JSIModulePackage, * provided the name of the dynamic library to load. */ -abstract class DefaultReactNativeHost protected constructor(application: Application) : - ReactNativeHost(application) { +abstract class DefaultReactNativeHost +protected constructor( + application: Application, +) : ReactNativeHost(application) { override fun getReactPackageTurboModuleManagerDelegateBuilder(): ReactPackageTurboModuleManagerDelegate.Builder? = @@ -38,6 +41,13 @@ abstract class DefaultReactNativeHost protected constructor(application: Applica null } + override fun getJSEngineResolutionAlgorithm(): JSEngineResolutionAlgorithm? = + when (isHermesEnabled) { + true -> JSEngineResolutionAlgorithm.HERMES + false -> JSEngineResolutionAlgorithm.JSC + null -> null + } + /** * Returns whether the user wants to use the New Architecture or not. * @@ -48,4 +58,14 @@ abstract class DefaultReactNativeHost protected constructor(application: Applica */ protected open val isNewArchEnabled: Boolean get() = false + + /** + * Returns whether the user wants to use Hermes. + * + * If true, the app will load the Hermes engine, and fail if not found. If false, the app will + * load the JSC engine, and fail if not found. If null, the app will attempt to load JSC first and + * fallback to Hermes if not found. + */ + protected open val isHermesEnabled: Boolean? + get() = null } diff --git a/packages/rn-tester/android/app/build.gradle b/packages/rn-tester/android/app/build.gradle index 1bfd0ebc649614..86d3646ba34ce8 100644 --- a/packages/rn-tester/android/app/build.gradle +++ b/packages/rn-tester/android/app/build.gradle @@ -138,9 +138,11 @@ android { productFlavors { hermes { dimension "vm" + buildConfigField("boolean", "IS_HERMES_ENABLED_IN_FLAVOR", "true") } jsc { dimension "vm" + buildConfigField("boolean", "IS_HERMES_ENABLED_IN_FLAVOR", "false") } } diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java index 5012a0e45e4d9d..98de09e81c0d7f 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java @@ -115,6 +115,11 @@ public List createViewManagers( protected boolean isNewArchEnabled() { return true; } + + @Override + protected Boolean isHermesEnabled() { + return BuildConfig.IS_HERMES_ENABLED_IN_FLAVOR; + } }; @Override diff --git a/template/android/app/src/main/java/com/helloworld/MainApplication.java b/template/android/app/src/main/java/com/helloworld/MainApplication.java index be113ec43969fd..3227c9e96996bb 100644 --- a/template/android/app/src/main/java/com/helloworld/MainApplication.java +++ b/template/android/app/src/main/java/com/helloworld/MainApplication.java @@ -37,6 +37,11 @@ protected String getJSMainModuleName() { protected boolean isNewArchEnabled() { return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; } + + @Override + protected Boolean isHermesEnabled() { + return BuildConfig.IS_HERMES_ENABLED; + } }; @Override