diff --git a/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java b/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java new file mode 100644 index 00000000000000..7e960263cc55da --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.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 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 d7450a14ce5c9e..d004da42d6475b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -66,6 +66,7 @@ public class ReactInstanceManagerBuilder { private @Nullable Map mCustomPackagerCommandHandlers; private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; + private JSInterpreter jsInterpreter = JSInterpreter.OLD_LOGIC; /* package protected */ ReactInstanceManagerBuilder() {} @@ -125,6 +126,31 @@ public ReactInstanceManagerBuilder setJSBundleLoader(JSBundleLoader jsBundleLoad return this; } + /** + * 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 + */ + public ReactInstanceManagerBuilder setJsEngineAsHermes(boolean hermesEnabled) { + if (hermesEnabled) { + setJSEngine(JSInterpreter.HERMES); + } else { + setJSEngine(JSInterpreter.JSC); + } + return this; + } + /** * Path to your app's main module on Metro. This is used when reloading JS during development. All * paths are relative to the root folder the packager is serving files from. Examples: {@code @@ -345,41 +371,35 @@ public ReactInstanceManager build() { private JavaScriptExecutorFactory getDefaultJSExecutorFactory( String appName, String deviceName, Context applicationContext) { - try { - // If JSC is included, use it as normal - initializeSoLoaderIfNecessary(applicationContext); - JSCExecutor.loadLibrary(); - return new JSCExecutorFactory(appName, deviceName); - } catch (UnsatisfiedLinkError jscE) { - // https://github.com/facebook/hermes/issues/78 shows that - // people who aren't trying to use Hermes are having issues. - // https://github.com/facebook/react-native/issues/25923#issuecomment-554295179 - // includes the actual JSC error in at least one case. - // - // So, if "__cxa_bad_typeid" shows up in the jscE exception - // message, then we will assume that's the failure and just - // throw now. - - if (jscE.getMessage().contains("__cxa_bad_typeid")) { - throw jscE; - } - // Otherwise use Hermes + // Relying solely on try catch block and loading jsc even when + // project is using hermes can lead to launch-time crashes especially in + // monorepo architectures and hybrid apps using both native android + // and react native. + // So we can use the value of enableHermes received by the constructor + // to decide which library to load at launch + + // if nothing is specified, use old loading method + // else load the required engine + if (jsInterpreter == JSInterpreter.OLD_LOGIC) { try { + // If JSC is included, use it as normal + initializeSoLoaderIfNecessary(applicationContext); + JSCExecutor.loadLibrary(); + return new JSCExecutorFactory(appName, deviceName); + } catch (UnsatisfiedLinkError jscE) { + if (jscE.getMessage().contains("__cxa_bad_typeid")) { + throw jscE; + } HermesExecutor.loadLibrary(); return new HermesExecutorFactory(); - } catch (UnsatisfiedLinkError hermesE) { - // If we get here, either this is a JSC build, and of course - // Hermes failed (since it's not in the APK), or it's a Hermes - // build, and Hermes had a problem. - - // We suspect this is a JSC issue (it's the default), so we - // will throw that exception, but we will print hermesE first, - // since it could be a Hermes issue and we don't want to - // swallow that. - hermesE.printStackTrace(); - throw jscE; } + } else if (jsInterpreter == JSInterpreter.HERMES) { + HermesExecutor.loadLibrary(); + return new HermesExecutorFactory(); + } else { + JSCExecutor.loadLibrary(); + return new JSCExecutorFactory(appName, deviceName); } } }