-
Notifications
You must be signed in to change notification settings - Fork 24.7k
Added additional builder method receiving arguments for using jsc or hermes to correctly decide which DSO to load at app startup. #33123
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
Added additional builder method receiving arguments for using jsc or hermes to correctly decide which DSO to load at app startup. #33123
Conversation
…hermes. Based on the arguments, the correct .so file can be loaded without relying on try catch to select the correct one.
Base commit: 7e8cce3 |
Base commit: 7e8cce3 |
@@ -66,9 +66,20 @@ | |||
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers; | |||
private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; | |||
private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; | |||
private String jscOrHermes = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you using an extra string here? A boolean would be sufficient
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am using it to keep the old logic intact.
In this way, apps already using the old logic and running fine won't need to change anything and those who were facing crashes can update based on the method I mentioned in the summary.
The empty case uses the old try catch logic and when the string is set, it uses the new logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be updated to use a boolean only if you want to mandate all apps to use the new approach and pass the value for hermesEnabled in the ReactInstanceManager :: builder method.
The old logic would be removed in that case. That is what we have done in our fork of react-native.
I have kept it as a string for legacy compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's update this to be a Boolean hermesEnabled = null
, with the current logic:
null
-> Use the previous logictrue
-> Load Hermes or failfalse
-> Load JSC or fail
@cortinico, are any updates required in the code? |
Hi @cortinico . Can you please check this PR as well. |
Not sure what was going on here. Could you try to rebase? I'll look into this PR next week 👍 |
Is the review done @cortinico |
Any update @cortinico? |
He @cortinico, are you reviewing this? |
Yup it's on me, I'll get back to this next week 👍 |
still not reviewed? |
Hey @cortinico, is this getting reviewed or not? |
@@ -66,9 +66,20 @@ | |||
private @Nullable Map<String, RequestHandler> mCustomPackagerCommandHandlers; | |||
private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; | |||
private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; | |||
private String jscOrHermes = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's update this to be a Boolean hermesEnabled = null
, with the current logic:
null
-> Use the previous logictrue
-> Load Hermes or failfalse
-> Load JSC or fail
else{ | ||
try { | ||
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; | ||
initializeSoLoaderIfNecessary(applicationContext); | ||
if(jscOrHermes.equals("hermes")){ | ||
HermesExecutor.loadLibrary(); | ||
return new HermesExecutorFactory(); | ||
} | ||
else{ | ||
JSCExecutor.loadLibrary(); | ||
return new JSCExecutorFactory(appName, deviceName); | ||
} | ||
} catch (UnsatisfiedLinkError e) { | ||
// linking library failed, so we print its stacktrace and | ||
// throw the exception | ||
e.printStackTrace(); | ||
throw e; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's try to refactor this a bit:
if (enableHermes == null || enableHermes == false) {
JSCExecutor.loadLibrary();
else if (enableHermes == null || enableHermes == true) {
HermesExecutor.loadLibrary();
}
There is no need to duplicate code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine, however, I seem to have lost the original repository from where I raised the PR. I would have to raise this PR again. I will ping the link to that PR here before closing this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure take your time 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @cortinico. I have raised a new and updated PR for the same here: #33952
Kindly review that.
I will close this PR once that one is reviewed.
Sorry for the late review. This fell through my desk. |
Hey @cortinico can you review the new PR #33952 |
Yup I'll close this |
Summary
The current implementation of getDefaultJSExecutorFactory relies solely on try catch to load the correct .so file for jsc or hermes based on the project configuration.
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 make use of an additional ReactInstanceManager :: builder method that accepts an argument while building ReactInstanceManager which can tell which library to load at startup in ReactInstanceManagerBuilder which will now have an enhanced getDefaultJSExecutorFactory method that will combine the old logic with the new one to load the dso files.
The code snippet in ReactInstanceManager for adding a new builder method:
The code snippet for the new logic in ReactInstanceManagerBuilder:
Suggested changes in any Android App's MainApplication that extends ReactApplication to take advantage of this fix
where HERMES_ENABLED is a buildConfigField based on the enableHermes flag in build.gradle:
def enableHermes = project.ext.react.get("enableHermes", true)
and then
Our app was facing a similar issue as listed in this list: https://github.com/facebook/react-native/issues?q=is%3Aissue+is%3Aopen+DSO. Which was react-native trying to load jsc even when our project used hermes when a debug build was deployed on a device using android studio play button.
This change can possibly solve many of the issues listed in the list as it solved ours.
Changelog
[GENERAL] [ADDED] - A second builder method in ReactInstanceManager.java:
[GENERAL] [ADDED] - A string varaible and a parametrized constructor in ReactInstanceManagerBuilder.java:
[GENERAL] [ADDED] - Modified getDefaultJSExecutorFactory method
Test Plan
The testing for this change might be tricky but can be done by following the reproduction steps in the issues related to DSO loading here: https://github.com/facebook/react-native/issues?q=is%3Aissue+is%3Aopen+DSO
Generally, the app will not crash anymore on deploying debug using android studio if we are removing libjsc and its related libraries in packagingOptions in build.gradle and using hermes in the project.
It can be like: