Skip to content

Added additional builder method receiving arguments for using jsc or hermes to correctly decide which DSO to load at app startup. #33952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12 changes: 12 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class ReactInstanceManagerBuilder {
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers;
private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder;
private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory;
private JSInterpreter jsInterpreter = JSInterpreter.OLD_LOGIC;

/* package protected */ ReactInstanceManagerBuilder() {}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -344,42 +370,36 @@ 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
String appName, String deviceName, Context applicationContext) {

// 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);
}
}
}