Skip to content
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

JSI (JavaScript Interface) & JSC (JavaScript Core) Discussion #91

Closed
kelset opened this issue Jan 24, 2019 · 124 comments
Closed

JSI (JavaScript Interface) & JSC (JavaScript Core) Discussion #91

kelset opened this issue Jan 24, 2019 · 124 comments
Labels
👓 Transparency This label identifies a subject on which the core has been already discussing prior to the repo

Comments

@kelset
Copy link
Member

kelset commented Jan 24, 2019

What's the current status of JSI? Read here

Intro

With this issue I'd like to try and create a "one stop" for all the information available around the JavaScript Interface, the unified lightweight general purpose API for (theoretically) any JavaScript virtual machine.

Currently, the default Javascript Virtual Machine used by React Native is JavaScriptCore (JSC) - it is used in WebKit.

Terminology

  • Fabric: just the UI layer re-architecture, to take full advantage of concurrent React architecture. (dedicated issue)
  • TurboModules: re-architecture of NativeModules, also using JSI. (dedicated issue)
  • CodeGen: a tool to "automate" the compatibility between JS and native side. (dedicated issue)

TL;DR

From @axe-fb's blogpost, here's a temporary description of the JSI (please consider that this is not yet finalized, it may change in the future)

Instead of using the bridge for queuing messages, the new architecture allows us to directly "invoke" (think RPC) Java/ObjC methods.
An analogy would be how we call DOM methods from JavaScript in the browser. For example, in the statement var el = document.createElement('div'); the variable el holds a reference not to a JavaScript object, but to an object that was possibly instantiated in C++. When JavaScript calls el.setAttribute('width', 100), we end up synchronously invoking the setWidth method in C++ that changes the actual width of that element.
In React Native, we can similarly use the JavaScript interface to invoke methods on UI Views and Native Modules that are implemented in Java/ObjC.

Available Materials

At ReactConf 2018 @axe-fb did a talk about React Native's New Architecture, which also explains the 3 concepts above: JSI, Fabric, TurboModule.

Recently the JSC was upgraded for Android, you can check the commit here and the related issue in the main repo.

On twitter, @karanjthakkar did a thread detailing how he integrated the new JSC in the Skyscanner app.

In Q1 2019, @kelset wrote a high level explanation in a blogpost: https://formidable.com/blog/2019/jsi-jsc-part-2/ and did a talk about the whole rearchitecture in April 2019 at React Edinburgh.

Over on twitter, @ericlewis published a sample repo with @chrfalch to showcase how to use directly C++ in a RN app (thanks to the JSI): https://github.com/ericlewis/react-native-hostobject-demo

@kelset also did a more in-depth talk at React Advanced London in Oct 2019: youtube recording & slides.

@Jarred-Sumner published a quick benchmark difference between a lib and a reimplementation using JSI twitter.

Libraries implemented using JSI

Blogposts

Q&A

This is also a place for questions related to this effort and its direction.

@kelset kelset added the 👓 Transparency This label identifies a subject on which the core has been already discussing prior to the repo label Jan 24, 2019
@kelset kelset changed the title JSI (JavaScript Interface) Discussion JSI (JavaScript Interface) & JSC (JavaScript Core) Discussion Feb 19, 2019
@Eyesonly88
Copy link

Eyesonly88 commented Mar 6, 2019

I have a couple of questions:

@Kudo
Copy link
Member

Kudo commented Mar 6, 2019

@Eyesonly88

Why doesn't React Native use the same exact JSC for iOS and Android?
AFAIK, Android JSC !== iOS JSC (correct me if I'm wrong)

Even at the same version of JSC, the Android implementation is different to iOS.
So that Android JSC will always not equal to iOS.
Furthermore, different iOS versions have different JSCs.

Is it even possible to use something else other than JSC for iOS? AFAIK, even Chrome doesn't use V8 on iOS: https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/zINHc4_dWhk

Yes, JSI brings a benefit for other platforms to use different JavaScript engine easier.
E.g. react-native-windows will use CharkaCore

BTW Chrome does not use v8 on iOS simply because they should follows App Store Review Guidelines

2.5.6 Apps that browse the web must use the appropriate WebKit framework and WebKit Javascript.

@Eyesonly88
Copy link

@Kudo interesting, thanks for the reply.

Yeah, regarding V8 on iOS, that's what I was wondering about, even if RN provided a way to use other engines, we're stuck with JSC on iOS due to App Store rules.

@Pikaurd
Copy link

Pikaurd commented Apr 4, 2019

@Eyesonly88 Please check @Kudo comment 2.5.6 Apps that browse the web must use the appropriate WebKit framework and WebKit Javascript. That focus "browse the web". Chrome is a web browser therefore google has to obey it.

@kelset kelset pinned this issue Apr 14, 2019
@sJJdGG
Copy link

sJJdGG commented Apr 15, 2019

@Pikaurd @Eyesonly88 Yeah I also think @Kudo is right regarding usage of V8 and theoretically it should already be possible to submit V8-powered RN-based apps to app store review process and getting accepted considering JSI efforts on RN side and more importantly the almost new announced JIT-less V8 on V8 side.

But really eager to see a practical example that's accepted to app store.

To me biggest benefit of V8 (In addition to other common reasons) could be V8 snapshots that should help boosting startup times considering parsing javascript is a significant chunk of overall code execution time.

@Eyesonly88
Copy link

Eyesonly88 commented Apr 15, 2019

@sJJdGG I'm happy to try it in one of my production apps, any guide on how would I go about doing that? (I'd do anything to improve startup time)

@sercand
Copy link

sercand commented Apr 16, 2019

@sJJdGG @Eyesonly88 ReactNative supports another bundle option which is ByteCode Bundle. It is not documented and I guess facebook uses it in their app. It must be be similar to V8 snapshots.

@aleclarson
Copy link

Does this have any implications for WebAssembly interop with React Native?

@kirillpisarev
Copy link

I still can't figure out how JSI will solve the problem with RN viewport size suspense in case of embedding RN within a native project? As i understand the flow now is like:

  1. init RN native
  2. init RN JS
  3. build JS shadow tree
  4. draw native

During all this time the viewport doesn't know the size of content that leads to UI problems.

So what will be changed to solve exactly this problem?

@kelset
Copy link
Member Author

kelset commented Jun 6, 2019

the viewport doesn't know the size of content that leads to UI problems

my understanding is that actually through JSI & Fabric, the viewport will know. But I may be wrong.

@kelset kelset unpinned this issue Jun 20, 2019
@oNaiPs
Copy link

oNaiPs commented Aug 2, 2019

Hi guys, we have been working on a JSI implementation for node.
Idea is to be able to re-use JS<->C++ modules that work in both React Native and Node/Electron. We think JSI has a great potential to become an API for cross-runtime implementations.

We also worked on a C++ template system that allows us to define C++ classes that map easier to a JS object, without any codegen tools.

We would like to share our effort with the community, but would like some guidance on how we could do that, perhaps with some mentoring from people that is involved with JSI implementation.

Thanks

@akshetpandey
Copy link

akshetpandey commented Sep 11, 2019

So I have some code using turbomodules but I understandably can't run it in remote debug mode since that runs with the remote chrome js engine. What is the workaround and what does the debug DX look like for JSI HostObjects and turbomodules?

@terrysahaidak
Copy link

terrysahaidak commented Sep 12, 2019

@akshetpandey I as remember, the current workaround is to have both JSI and the bridge and switching between them when you debugging.

When you're using JSI, your module available using global object, but once you've switched to a remote debugger, use NativeModules instead (if you have bindings).

You can check out @chrfalch's reanimated JSI example (might be outdated a bit): software-mansion/react-native-reanimated@master...chrfalch:jsi-methods

@elicwhite
Copy link

When using JSI, we currently recommend using safari to debug the JSC instance running on device, or on Android if you are using Hermes, you can connect Chrome to the instance of Hermes running on device.

@terrysahaidak
Copy link

Is there any article/example/docs on how to properly use JSI on Android? Maybe there is already module in current master which uses JSI on Android? I would appreciate any link.

@kelset
Copy link
Member Author

kelset commented Oct 21, 2019

@terrysahaidak I think that the closest thing to that atm is: ericlewis/react-native-hostobject-demo#4

@terrysahaidak
Copy link

This looks cool and I've already seen it, but not quite sure how to trigger Java code from C++. By the way, does CodeGen is usable in any kind already?

@kelset
Copy link
Member Author

kelset commented Oct 21, 2019

I'm not sure about it being usable - afaik it's still under heavy development. But you can check it out here -> https://github.com/facebook/react-native/tree/master/packages/react-native-codegen

@chrfalch
Copy link

@tomduncalf added a pull request to Eric and my repository showing how to add support for JSI on Android, don't know if that can be used?

ericlewis/react-native-hostobject-demo#4

@terrysahaidak
Copy link

Here is my attempt to get JSI working on android. And yeah, JSI definitely works, but also JNI works as well! So yeah, we can call Java from JS right now:
https://github.com/terrysahaidak/host-object-test/blob/master/libs/android-jsi/test-jsi/src/main/cpp/TestJSIInstaller.cpp

@mfbx9da4
Copy link

mfbx9da4 commented Jan 31, 2022

I've noticed some issues with using promises together with async JSI functions. I've raised an issue facebook/react-native#33006

Has anyone here experienced something similar?

@yaaliuzhipeng
Copy link

Is there any way to call the callback passed from javascript async .
I have a java method with async listener like below

static public void identifyLanguage(String text) {
        LanguageIdentifier languageIdentifier = LanguageIdentification.getClient();
        languageIdentifier.identifyLanguage(text)
            .addOnSuccessListener(v -> {
               
            })
            .addOnFailureListener(e -> {

            });
    }

I invoked the c++ method from javascript through jsi and the c++ method called this static java method . the problem is I passed in an anonymous callback from javascript to receive calling result , but as you can see the result from java is asynchronous . the registered c++ method cannot get the result synchronous . how can I invoke the passed in callback once the SuccessListener invoked ?

@stephenkopylov
Copy link

stephenkopylov commented Feb 10, 2022

@yaaliuzhipeng the main thing you need to achieve this is react::CallInvoker. When you'll get all you need to do is something like this:

#include <ReactCommon/CallInvoker.h>
#include <ReactCommon/TurboModule.h>
#include <ReactCommon/TurboModuleUtils.h>

static Value foo(Runtime &rt, TurboModule &turboModule, const Value *args, size_t arg_count) {
	std::weak_ptr<CallbackWrapper> callbackWrapper = react::CallbackWrapper::createWeak(args[0].getObject(rt).getFunction(rt), rt, turboModule.jsInvoker_);
	
	callYourJavaMethodWithAsyncCallbackAsArgument([callbackWrapper](){
		auto strongWrapper = callbackWrapper.lock();
		if (!strongWrapper) {
			return;
		}
		
		strongWrapper->jsInvoker().invokeAsync([callbackWrapper]() {
			auto strongWrapper2 = callbackWrapper.lock();
			if (!strongWrapper2) {
				return;
			}
			
			strongWrapper2->callback().call(strongWrapper2->runtime()); //calling your async callback
			
			strongWrapper2->destroy();
		});
	};
	);
	
	return 0;
};

PS: I'd recommend you to use CallbackWrapper - it's really useful if you are not sure that JSRuntime (ant all it's objects including your callback) is still alive

PPS: Don't forget to destroy all your callbacks in TurboModule's destructor

PPPS: as I mentioned before - here's a good example of TurboModule with JSCallInvoker in it

@laptou
Copy link

laptou commented Feb 14, 2022

How do I get a shared_ptr to the JS runtime on Android? It seems to be exposed as a shared_ptr on iOS, which is great b/c I can convert it into a weak_ptr and this lets me know when the runtime is destroyed, but on Android it seems that I just get a raw pointer with no easy way to track the lifetime of the runtime.

@intergalacticspacehighway
Copy link

intergalacticspacehighway commented Feb 20, 2022

Is there any recommended way to release native resources held by the HostObject when it's being garbage collected? (Assuming HostObject gets GCed when there are no references to them in JS world and when the garbage collector runs)

@mrousavy
Copy link
Member

Yes, the HostObject's C++ destructor.

@intergalacticspacehighway
Copy link

intergalacticspacehighway commented Feb 20, 2022

Great. Thank you @mrousavy. Tried putting a destructor and noticed it gets called sometimes but not consistently when we release the variable references from JS. Maybe because it's still not garbage collected?
In that case, I guess it would be safer to provide some function in HostObject that we can manually call to release resources? Let me know if I am understanding it correctly.

@mrousavy
Copy link
Member

Yes, JS memory management works different than C++' memory management. The HostObject only gets deleted when the GC runs (which btw might even be on another Thread, so be careful when doing JSI or JNI stuff).

See react-native-vision-camera's Frame Host Object for an example :)

@laptou
Copy link

laptou commented Feb 28, 2022

Is the lifetime of the CallInvoker tied to the lifetime of the Runtime? As in, if I schedule a callback using invokeAsync, is it guaranteed that the runtime will still exist when the callback is called?

@laptou
Copy link

laptou commented Mar 1, 2022

@DominickVale install React Native into a node_modules folder, then add the following paths to your header search path:

node_modules/react-native
node_modules/react-native/React
node_modules/react-native/React/Base
node_modules/react-native/ReactCommon/jsi
node_modules/react-native/ReactCommon/callinvoker

You can also get the same headers by downloading the Hermes source code (they are located in the API folder).

@laptou
Copy link

laptou commented Mar 1, 2022

How do you get a reference to the RuntimeExecutor on iOS? It's available via CatalystInstance on Android, but I can't find the analogous method on iOS.

@DominickVale
Copy link

Hi, what would be the correct way to have an array of objects constantly being updated and the js side being notified of those changes? All the attempts I've made end up in crashes. I tried callbacks, i tried manually getting the data from a js loop etc. They are all not working at higher speeds (for example less than 50ms)

@JiriHoffmann
Copy link

Hi, I was wondering... Is there was a way to pass an ArrayBuffer from C++ to the JS side? I know there is the getArrayBuffer method to get ArrayBuffer from JS, but I couldn't find anything for the other way around.

@mhorowitz
Copy link

Hi, I was wondering... Is there was a way to pass an ArrayBuffer from C++ to the JS side? I know there is the getArrayBuffer method to get ArrayBuffer from JS, but I couldn't find anything for the other way around.

Not currently. There was discussion of this in facebook/hermes#419 (comment) in the past. It's not something on our roadmap, but we would be happy to advise on what we would want to see in a PR.

@mfbx9da4
Copy link

mfbx9da4 commented Mar 17, 2022

@JiriHoffmann Does react-native-buffer do what you need?

@DominickVale I've also experienced performance issues with async JSI functions

@JiriHoffmann
Copy link

@JiriHoffmann Does react-native-buffer do what you need?

Not really, it uses memcpy which is the same thing I do.

Hi, I was wondering... Is there was a way to pass an ArrayBuffer from C++ to the JS side? I know there is the getArrayBuffer method to get ArrayBuffer from JS, but I couldn't find anything for the other way around.

Not currently. There was discussion of this in facebook/hermes#419 (comment) in the past. It's not something on our roadmap, but we would be happy to advise on what we would want to see in a PR.

@mhorowitz I would love to give it a try, although I'm not sure if my JSI and C++ knowledge is up to par yet. Maybe a new issue would be helpful so that others can take a look at it as well?

Also I created two JSI libraries if you want to add them to the list:

@DominickVale
Copy link

DominickVale commented Mar 26, 2022

@JiriHoffmann Does react-native-buffer do what you need?

@DominickVale I've also experienced performance issues with async JSI functions

I'd love to only get performance issues. I literally cannot find a reliable way to get state updates from the c++ side of things. Whether i call a c++ function from javascript every 20ms or i call said javascript function from state hooks on the c++ side it crashes in a non-deterministic way 80% of the time (at least to me). The worst part is actually the fact that it works most of the time but it crashes in this random way. I tried debugging but the stack trace doesn't make sense to me as it's just internal JSI stuff and nothing regarding what I'm doing natively.
What i need to do is pretty simple... I just want real-time data from the c++ side. It's been a month and still no solution.

@kelset
Copy link
Member Author

kelset commented Mar 28, 2022

@DominickVale that sounds like a big problem - can you try to isolate it into a repro project and report it (similarly to what @mfbx9da4 did) so that it can be investigated further?

@chrfalch
Copy link

@DominickVale Could there be an issue with accessing the Javascript JSI objects from other threads than the JS thread? This could be a source of unreliable crashes like this.

@yotamishak
Copy link

yotamishak commented Apr 4, 2022

Not sure if this is the right place for this, if so it would be great if you could refer me to where this can be discussed

I wanted to ask how native modules in the new architecture work compared to "jsi modules".

"jsi modules" below

Libraries implemented using JSI

Will all new native modules (turbo modules) essentially be "jsi modules"?

Is there any need for c++ code in order to implement this or are codegen declarations on the js side enough for synchronous calls to native functions out of the box?

Does calling native function synchronously mean that return values are also returned synchronously ( as opposed to promises or callbacks)?

From my understanding, the bridge will still exist for backward compatibility, so how will migration of legacy native modules be handled? Is it enough to generate a codegen schema for the modules to redirect calls to these modules through the jsi as opposed to the bridge?

I don't want to overwhelm with questions but I feel as if the answers to all of these questions are related...
Thanks in advance!

@mrousavy
Copy link
Member

mrousavy commented Apr 4, 2022

Hey @yotamishak, I spotted my libs there! 😄

"JSI Modules" is what I call my libs that use the bare JSI C++ API to provide fast and synchronous bindings from JS to native (C++).

Turbomodules is an abstraction built ontop of JSI to make it easy for everyone to write native modules that are powered by JSI, without having to touch C++ code.

So yes, Turbomodules (the new native module system for React Native) will use JSI under the hood, just like my libraries. It's just a bit abstracted/wrapped to make it easier, since my JSI Modules are a bit complex in code (lots of C++)

@yotamishak
Copy link

@mrousavy thanks for the quick response
I'm well familiar with your libraries and have been trying to pick out part of your code to find a Jsi solution to my use case.

I noticed this repo https://github.com/mrousavy/react-native-jsi-library-template of yours.

So using the new architecture I wouldn't need to use this template at all correct? I would get all of this functionality out of the box?

@mrousavy
Copy link
Member

mrousavy commented Apr 5, 2022

Yea, when using Turbomodules you won't need my jsi-library-template. Instead, take a look at this guide.

There are some parts that are still not covered by Turbomodules, e.g. custom JSI HostObjects, shared memory ArrayBuffers, and multithreaded JSI Runtimes - which is why MMKV or VisionCamera (the Frame Processors Part specifically) cannot be migrated to a Turbomodule (yet).

I've been investigating solutions that allow you to create JSI HostObjects with the Java/ObjC APIs on Turbomodules and pass them around like normal types (boolean, number, string, object, array), maybe I'll open a PR to react-native core soon

@fwon
Copy link

fwon commented Jun 8, 2022

@Kudo

2.5.6 Apps that browse the web must use the appropriate WebKit framework and WebKit Javascript.

If so, why Hermes engine is supported in iOS, does it means that we should use Hermes for JSI & JavasScriptCore for inner WebView. Then we must keep two engine in RN iOS project?

@kelset
Copy link
Member Author

kelset commented Jun 27, 2022

hey folks, since we have an official deep dive discussions section open on this in the ReactWG New architecture, let's close this one and make the conversation progress over there: https://github.com/reactwg/react-native-new-architecture/discussions/categories/deep-dive (there are also some other sections, like Q&A, to ask more specific questions with an easier UX for replying)

Remember that you can apply to the WG (in case you are not in it yet) by applying to the form in the readme.

@kelset kelset closed this as completed Jun 27, 2022
@react-native-community react-native-community locked as resolved and limited conversation to collaborators Jun 27, 2022
@kelset kelset unpinned this issue Jun 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
👓 Transparency This label identifies a subject on which the core has been already discussing prior to the repo
Projects
None yet
Development

No branches or pull requests