The purpose of this issue is to create a "single point of truth" regarding the efforts related to upgrading the JSC used by the react-native
repo/project, in order to coordinate and recap all the efforts connected to this subject.
The JavaScriptCore (JSC), basically, allows Android developers to use JavaScript natively in their apps - you can read here for more details about how react native uses engines.
Currently, react-native
bundles in the Android app android-jsc, which is outdated and relies on old native tooling (ex. NDK v10).
Upgrading the JSC would improve greatly performances for react-native
apps running on Android, and allow support for x64 builds on Android apps.
Which links back this to the main "Roadmap of future Android(s)" issue #19297 - quote:
In anticipation of future Android devices that support 64-bit code only, the Play Console will require that new apps and app updates with native libraries provide 64-bit versions in addition to their 32-bit versions. This can be within a single APK or as one of the multiple APKs published.
Currently there are two approaches possible, keep relying on the android-jsc bundled but changing some other configs:
[WIP] Android 64-bit #17640
add arm64 and x86_64 support #18754
Which seems to not be the way to go, given that internally FB doesn't use it.
The alternative is to move to the JSC fork maintained by some important players in the core & community:
React Native for Android is incompatible with 3rd-party 64-bit libraries #2814
[...Array(5).keys()] not working in Android #18426
React Native apps fail to build since NDK update to r17 (due to removed support for ARMv5/armeabi) #19321
potentially (as @kmagiera wrote in core), this unfortunately would bring some significant footprint penalty: JSC built with intl enabled takes roughly 5M more than without it. This needs to be multiplied by the number of CPU archs included (e.g. x86, armv7, arm64, x86_64), which leads to an "Hello World" base app to ~40MBs - unless split apk is enabled. But this it potentially "automatically" fixed by Google via their new Android App Bundle feature.
Again, this issue is for discussing and keeping everyone updated with decisions around this subject (based on discussions happened with the core team and open issues in the repo). Please avoid going OT.
Great job creating a centralized issue for this @kelset .
The question of whether to include Intl
support is a good one. The JavaScriptCore on iOS 10.0+ has Intl
support, so it would be good for consistency to have the Android one include it as well. However I can see how the increase in the binary size makes that problematic.
I believe you are mistaken...the "FB doesn't use it internally" appears to be referring to the updated SoftwareMansion JSC. That is, FB doesn't want to push an updated JSC to open source that they don't use internally, and the one they use internally is android-jsc
. At least, that's how I read it.
Re: newer JavaScriptCore, I would imagine it is basically a non-starter to update JSC past the version shipped by the RN minimum supported iOS version...otherwise one could use JS features on Android that don't work on some subset of iOS devices and cause all sorts of annoying, hard to debug issues.
FWIW, I have patches out for both the two issues in Buck blocking RN:
For "React Native for Android is incompatible with 3rd-party 64-bit libraries": https://github.com/facebook/buck/pull/1889
For "React Native apps fail to build since NDK update to r17 ": https://github.com/facebook/buck/pull/1894
I prefer https://github.com/facebook/react-native/pull/19536. Also with this I think we can reconfig babel to generate modern js. @rafeca
For app bundle size, x86 and x86_64 should not bundled to the apk when you push the apk to market. Because real phone are using arm cpu.
@gengjiawen You can find X86 devices out in the market but they're so much rarer, especially in modern Android versions. It's mostly ignorable, my company only uses it for emulator builds.
Yes, ignorable is the precise desription.
@LegNeato we're not using either JSC at Facebook, therefore there's no reason to force OSS to use android-jsc and we're happy to help unblock folks here.
Good to know...but, it would be weird if the JSC of react native was newer than the minimum iOS version's JSC, right? Then you would have Android and modern iOS working, and older iOS devices degrading in quality and compatibility. But perhaps that's ok?
we're not using either JSC at Facebook
Wait, what are you guys using as the alternative then?
@hey99xx I can't answer that just yet, but hopefully soon we can share more information. Let's keep this issue scoped to what needs to be done to allow open source to use an arbitrary JSC.
@LegNeato
Then you would have Android and modern iOS working, and older iOS devices degrading in quality and compatibility.
It is more or less already the case. Many devs start coding for ios and later builds for android. So short example: we started to use mobx 5, it requires native Proxy support. All worked good until we run our app on ios 9 (iPhone 4s) and bum!... we just discovered that ios 9 JSC too old for Proxy support and this can't be polifilled. For Android we know that we can bundle our own fresh enough JSC. But for ios, we can't find ready to use solution.
So we already have modern iOS working, and broken old devices (that can't run iOS > 9).
(PS. Yes I seen JSCWrapper
code and it seems that FB can use custom JSC on iOS. But building JSC itself for iOS seems like hard work for most devs.)
Some thoughts about possibility to have one JS engine for build on any platform. If this possible it will be big benefit for manage package process.
Just idea (sorry if it looks like offtopic), currently we have only two JS engines which have enough features and can be built for Android/iOS:
cc @DanielZlotin
It got easy to build JSC using script from its repo. See https://trac.webkit.org/wiki/JSCOnly and http://constellation.github.io/blog/2016/05/02/how-to-build-javascriptcore-on-your-machine/
@dulmandakh Do you really built it for android and ios with this script? )
@vovkasm nope, but from the repo history I thought that the script is actively maintained
Ok, seems it contains some condition logic for ios... will try it soon... (I currently evaluate the possibility to build ios version of JSC to allow use of Proxy api in our applications which required by latest mobx versions)
@dulmandakh I have built android-jsc and jsc-android-buildscripts. Both require lots of work for android specific. And you also need to build an icu. Both take very long to build.
jsc-android-buildscripts recently updated their readme regarding minSdkVersion being 21 ( https://github.com/react-community/jsc-android-buildscripts/commit/fd68310e67b236ce4bc6b4677f9ea8acf53a33f1 ).
Will this have an impact in future of RN, should we expect the same in this repo too? Will whatever Facebook use internally, if ever released, come with the increased minSdkVersion?
New apps can easily accept this new requirement, but for existing brownfield apps with only some pages written in RN, I fear this might be a obstacle of updating JSC versions :(
On binary size. I'll say that should not be a primary concern in stopping 64 bit adoption, as Google is going to require it in 2019. Different methods of binary size reduction by splitting or otherwise should be integrated or documented for users as side issues. I for one am willing to take the binary size penalty, given compatibility and performance benefits for 64 bit.
babel-preset-react-native
has 27 plugins, after upgrade JSC to 224109.0.0 , in my tests, only 6 plugins/transformers is needed. The latest JSC has better native support for ES2015+ language features(eg: async-await, Proxy). So my suggestion is if JSC is upgraded, we should think about how to customize the babel-preset-react-native
. Not only for better performance, debugging experience will be much better(Currently, the async-await
use regenerator
runtime, the call-stack is very bad for debugging).
That's a good point @dongyuwei . Once the RFC process is up for React Native, I'm going to propose a change to babel-preset-react-native
to add parameters that define the minimum supported iOS and Android JSC version of the app. That way, the babel plugins can be tailored to the specific runtime support of the JSC. It could make use of babel's own env
preset (https://babeljs.io/docs/en/babel-preset-env).
Here's an example of the idea, describing the app with a minimum iOS 10.3 version and Android using the latest JSC.
{
"presets": [
["react-native", {
"targets": {
"ios": "10.3",
"android": "224109.0.0"
}
}]
]
}
Long term, the plan should be to start using default V8 bundled within Android when using React Native. When RN Android was launched a year back, it supported Android versions down to API19 because of which it made sense to bundle a custom JSC binary into each and every RN app that is shipped to Android today. As you can imagine, this is not ideal.
With compilers like Babel and the babel-preset-env
it should be possible to selectively compile JS given the feature set of the target JSC vs. V8. If we are anyways going to put in effort towards 64-bit support, I would believe it worthwhile to focus directly on V8 support rather than simply upgrading bundled JSC on Android.
If upgraded to Latest jsc,and if we use Proxy
,how to polyfill it in lower IOS?
I was able to compile RN with latest NDK release (17b), and created a PR for it (https://github.com/facebook/react-native/pull/20357). I can compile and run RNTester app without any issues.
I think it might be better to bump NDK and use it to build JSC with it, because next NDK release will drop GCC and GNU STL, making CLANG and libc++ only supported option.
@paramaggarwal I don't think it will be possible for RN to use the default V8 bundled with Android. According to the NDK documentation, V8 is not available as an API to use from C/C++ code.
https://developer.android.com/ndk/guides/stable_apis
I think it's a similar situation to the ICU library, which is also in Android but not available to be used. That's why the JSC build needs to include ICU as well, inflating the size of the app's binary.
Using JavaScriptCore also has the benefit of aligning the runtime with that of iOS, as long we target JSC versions that are closing aligned with a production iOS implementation.
Any updates on this? Thanks!
maybe update jsc building with clang first, then update to new version. Also Microsoft is working on v8 for android with jsi launched.
@gengjiawen do you have a link to more information about Microsoft's effort for V8 with JSI? Can't find anything out there.
@dvicory I have no clue of the progress either, hope to hear from them soon
On the other hand, we are planning using the new jsc.
A sample if anyone interested: https://github.com/gengjiawen/ReactNativeNewJsc.
It's now with native es6 feature and async await support.
Could RN provide an adapter of js engine? It make more easy to switch anothes.
@gengjiawen What do you think about possibility to have same version of JSC on iOS instead of the system?
@vovkasm If you set a minimum iOS target for your app to 10.3 or 11.0, the bundled JavaScriptCore will also have native ES6 and async/await support. It's probably not worth bundling your own JSC unless you need support for very new features (e.g. BigInt
).
@newyankeecodeshop There is devices still in use, that always will be at iOS 9 :-(
It is iPhone 4s and some tablets...
And I think it is generally good to have consistent behavior across platforms to not think about compatibility at least on javascript level... I seen some code in RN that allows to use custom JSC for iOS, but it is undocumented and hard to understand for me...
What do you think about possibility to have same version of JSC on iOS instead of the system?
I am not master in iOS, but it maybe difficulty for iOS.
Also, if you are interested, you can see this issue https://github.com/facebook/metro/issues/190 for how to configure different babel rules for platform.
@vovkasm There are iOS 9 devices in use yes, but your app doesn't have to support them. The apps my company build only target the last two iOS releases. It's similar in the web app world, where browser apps will sometimes only support the last two major releases of each browser.
Just because RN supports iOS 9 (and Android API 16) doesn't mean every app needs to target those minimums.
Okay, with lots of help from community, I think we make significant progress on new JSC which bundle 64bit support same as on master. Full detail at https://github.com/gengjiawen/ReactNativeNewJsc.
For now you can try all native es6 and async await feature on android with two steps. Also you can tweak iOS bundle if you target iOS 10 or later.
While there is no solution right now maybe it is reasonable to create "React Native's JavaScriptCore: The missing parts"?
It can be repo with known issues and possible solutions to them. Because, I think, developer should be aware ahead of time about this tricky problems (hours or days wasted on figuring out why code runs in debugger but not on a device is not fun).
While there is no solution right now
There are solutions, and also we are planning to have the new JSC in the main repo too (probably by 0.59).
@kelset yeah, but you should search for them yourself and solution are not so trivial (compile your own JSC?) or so trivial you think it already should be solved by RN (global polyfills?). Docs has info only about different js engines, there are no info about known issues and inconsistencies.
we are planning to have the new JSC in the main repo too (probably by 0.59).
It would be great.
Docs has info only about different js engines, there are no info about known issues and inconsistencies.
Would you mind adding that list of "known issues and inconsistencies" as a PR to that page in the docs then? Probably it would help I feel. There is even an open issue over there to cover the purpose of listing the known issues -> https://github.com/facebook/react-native-website/issues/564 (it was meant for components but you can follow the template suggested anyway)
@kelset looks like great idea. I try to find as many issues as I can. Where I can find current version of JSC shipped with RN? Maybe we can use caniuse.com db to list missing parts?
see previous comment in this issue: https://github.com/facebook/react-native/issues/19737#issuecomment-439683552.
@fotonmoton awesome - thanks for helping out 😊 Feel free to DM me over on twitter if you need more info/help/anything.
@fotonmoton a couple of issues that probably fits well in the list you are working on:
@DanielZlotin successfully upgraded the JSC in https://github.com/facebook/react-native/commit/f3e5cce4745c0ad9a5c697be772757a03e15edc5. Based on the current release cycle, it looks like @kelset's estimate will hold out: 0.59 will be the first release with the upgraded JSC.
As of https://github.com/facebook/react-native/commit/f3e5cce4745c0ad9a5c697be772757a03e15edc5, React Native apps on Android will automatically use the new JavaScriptCore (with x64 support). Closing as the commit is on master and due to be picked up in 0.59 or some other release.
Very happy this change is making its way to release.
Are there any basic (or ideally comprehensive) performance breakdowns before & after that the community can view for large perf-related changes like this?
Just saying "it greatly improves performance" is definitely an anti-pattern in perf-related development.
Are there any basic (or ideally comprehensive) performance breakdowns
See https://github.com/react-native-community/jsc-android-buildscripts/blob/master/measure/README.md
Also it's not just about performance, it has the modern language features, and 64-bit support.
Thanks @hey99xx!
Im looking for a possibility to use my own build of jsc. Is there any way to override the downloadJsc task to use my own version?
we use another version of libc++shared in a ndk module and therefore we rebuilt jsc with this dependency. @hramos
@michaelknoch
You may try gradle packagingOptions.pickFirst
.
There is a solution for me to adopt JSC from jsc-android-buildscripts after RN 0.59.
https://github.com/react-native-community/jsc-android-buildscripts/pull/91
Most helpful comment
Long term, the plan should be to start using default V8 bundled within Android when using React Native. When RN Android was launched a year back, it supported Android versions down to API19 because of which it made sense to bundle a custom JSC binary into each and every RN app that is shipped to Android today. As you can imagine, this is not ideal.
With compilers like Babel and the
babel-preset-env
it should be possible to selectively compile JS given the feature set of the target JSC vs. V8. If we are anyways going to put in effort towards 64-bit support, I would believe it worthwhile to focus directly on V8 support rather than simply upgrading bundled JSC on Android.