Closed kelset closed 2 years ago
I have a couple of questions:
Android JSC !== iOS JSC
(correct me if I'm wrong)@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.
@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.
@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.
@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.
@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)
@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.
Does this have any implications for WebAssembly interop with React Native?
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:
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?
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.
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
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?
@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): https://github.com/kmagiera/react-native-reanimated/compare/master...chrfalch:jsi-methods
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.
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.
@terrysahaidak I think that the closest thing to that atm is: https://github.com/ericlewis/react-native-hostobject-demo/pull/4
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?
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
@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?
https://github.com/ericlewis/react-native-hostobject-demo/pull/4
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
I've been experimenting with JSI and enjoying the results but I can't figure out how one is expected to resolve a promise from a native worker thread. A reference to the runtime is required to invoke resolve
or reject
but the reference available when the promise is created is not valid for use within the worker thread. Is there an example somewhere of how this should be done?
@sdc395biosite You can take a look at some of the conversions between JSI <-> JavaScript in the React Native repo here: ReactCommon/turbomodule/core/TurboModuleUtils.cpp
.
@chrfalch Thanks for the rapid response. I've read through the code but I'm not sure what I'm supposed to see. Is JSCallInvoker
the key? Does this mean calls to JS from JSI still use the bridge? I was expecting to be able to use something like JNI's AttachCurrentThread or that the runtime would block my thread, queue the call to JS for the next process tick, and then release my thread with the result.
Is there any reference documentation for JSI? I have been looking for something comparable to the N-API docs and found nothing with that level of reference material in it.
@WillerZ as I understand it, there are no reference docs yet because JSI is still somewhat in flux. For now you just have to look at header files and example code (e.g. RNTester) I’m afraid
ditto what @tomduncalf is saying, the jsi header file is probably the best documentation atm.
Could I get a quick clarification on that? Is JSI substantially in flux, or is is just undocumented? E.g. is there a documentation problem to be solved, or is it so fluid that it's not worth anyone pursuing?
On Mon, Jan 6, 2020 at 5:00 AM Lorenzo Sciandra notifications@github.com wrote:
ditto what @tomduncalf https://github.com/tomduncalf is saying, the jsi header file is probably the best documentation atm.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/react-native-community/discussions-and-proposals/issues/91?email_source=notifications&email_token=AC5C774CWEQM5AVZJNRHHSLQ4MFLJA5CNFSM4GSGGK62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIFD6PY#issuecomment-571096895, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC5C772VRHYQBCOX3KQXV23Q4MFLJANCNFSM4GSGGK6Q .
I think that the last word around this should be given by someone from the FB on this.
But I feel it's quite stable; according to the commit history of the folder it's been lightly touched in the last few months.
That said, I wouldn't consider it "officially stable" until there is documentation on the main website.
JSI is a requirement to implement Fabric, which is required to get async rendering with suspense and all those new goodies from React to React Native.
I don't think there are any plans for pushing JSI as the new way of building native modules in the near future - tooling problems like debugging under Chrome not working with JSI modules makes JSI a bit developer unfriendly.
Hi, I'm the main author of JSI, and @TheSavior asked me to comment on this thread.
I would say that JSI is mostly stable. We have no plans to make substantive changes, but we're not ready to declare the API perfectly "stable", either. We have some ideas for improvements, but they are not fully baked, and may require some iteration.
The main improvements we've discussed internally are around making HostFunction a more robust API. For example, the arguments are passed as just a pointer and length which is simple and familiar, but we've seen bugs where people forget to do bounds checks. So we may choose to introduce a object to hold arguments in order to make it possible to do validation.
There has also been interest in making an ABI-stable variant of JSI, which would benefit people shipping binary modules. We're still pinning down what the exact requirements are and how this would affect JSI.
I hope this explains what the current maturity level of JSI is.
The question of documentation has come up here, and also in the Hermes github repo. Questions there tend to be about JSI generally or outside of the context of RN, but the answers should still be mostly useful to people writing RN native modules. https://github.com/facebook/hermes/issues/43#issuecomment-512558215 is something I wrote early on, which includes a pointer to the JSI header file (which is still the best detailed documentation).
Another useful file is the JSI tests which you can find at https://github.com/facebook/hermes/blob/master/API/jsi/jsi/test/testlib.cpp. This file uses Runtime, HostObject, and HostFunction extensively, but because it's trying to be comprehensive, not everything in there is actually a practice I would recommend. All that code will run with JSC or Hermes.
@mhorowitz thanks very much for the detailed update!
We have developed quite a tight integration between our C++ audio engine (using the JUCE framework) and React Native UI, with features like automatic state synchronisation, so JSI is definitely of interest to us in terms of both making the integration more performant, and cleaner in implementation. I look forward to trying out JSI in the near future!
If you’re interested to learn a bit more about our C++/RN integration, I go into more detail about it in this talk about how we built the ROLI LUMI app with React Native, JUCE and Unity from last year’s Audio Developers Conference: https://m.youtube.com/watch?index=42&v=bsy0-mHcS4Y&list=LLgksgv5Myug8PARo0Q2znaQ
So I've not made any progress figuring out how I'm meant to make a call back to my JavaScript from a native thread using the JSI. Assuming I'm using JSC, it seems to me that I need to acquire the JSGlobalContextRef held by the JSCRuntime object supplied to my JSI functions (as a jsi::Runtime). The obvious problem being the lack of any way of invoking JSGlobalContextRetain.
I don't mean to pollute this discussion but my problem does seem to be on-topic as the JSI seems to lack an API for safely keeping a reference to the JSC context in which my JavaScript lives.
Before JSI, our project was directly accessing to JSC by getting private JSGlobalContextRef from the bridge. I was defining lots of native functions in Rust and registring them to the global context. We have native functions for filesystem, sqlite, HTTP server, zip/unzip, download files for both iOS and Android. After JSI, we have only c++ API with multiple js backend, therefore, we can't use it directly anymore. To solve this, I created a "jsi-c" library which mostly mimics JSCore C API on JSI. Now my rust library links to jsi-c with lots of helper rust macros.
A usage example:
jsi_ffi! {
pub fn fs_exists(path: String) -> bool {
PathBuf::from(path).exists()
}
}
#[no_mangle]
pub unsafe extern "C" fn register_env(ctx: jsi::sys::JSIRuntime) {
let mynative = jsi::Object::new(ctx);
mynative.set_fn_property("fs_exists", Some(fs_exists));
jsi::Object::global(ctx).set_property("$mynative", mynative.into_value());
}
then call register_env function on swift/objc at RCTJavaScriptWillStartExecutingNotification.
My current biggest issue with JSI is it does not have the typed array interface. Javascript Core has it on iOS 10+ and also Hermes has it but JSI has only ArrayBuffer interface. Without a typed array interface, we can't reuse a single typed array for performance reasons and constantly convert between Uint8Array and ArrayBuffer.
The Hermes team maintains JSI. You can ask about TypedArray support on the Hermes repo
Can you clarify what TypedArray functionality is missing from JSI? AFAICT, it is possible to do everything.
how about make JSI a separated shared lib for community, RN and other libs can depend on it, we can make contribution to JSI
I made a separation about JSI from react-native and repository is https://github.com/acton393/JSI . The sample is https://github.com/acton393/JSISample which reference on https://github.com/ericlewis/react-native-hostobject-demo
@acton393 a complication is that any changes in the JSI lib has to be mirrored by changes in the JSI implementations for the different VMs. Facebook is currently maintaining the JSC and Hermes one, and there is at least one for V8 (from Microsoft I believe).
How would the process of adding new functionality work?
@tmikov JSC VM runtime is ready now (copy from Facebook), iOS sample using JSC runtime next step I will finish V8 runtime for JSI.
I will update here if any changes
@mhorowitz Is it possible to define a JavaScript class with JSI? I looked ever jsi.h and a few existing examples I could find, and while it is quite straight forward to define object instances, I couldn't figure out how to define a class. I thought maybe I could just define a function, and then new
that function in JavaScript in the same way I could if implemented directly in JavaScript, but that just resulted in an error ("TypeError function is not a constructor").
@oNaiPs it sounds like you were adapting NAPI to JSI, so I'm guessing you may have already solved this problem. We have some existing code that uses NAPI to interop between C++ and JavaScript (which relies on defining JavaScript classes), and we're trying to adapt it to JSI so we can use it in React Native. Any learnings you can share?
@ryantrem JavaScript classes are just syntactic sugar over functions, prototypes, and properties. You can do all that in JSI. The main thing you can't currently do is create a Function with a [[Construct]] internal method. In practice, the only real limitation that creates is you can't call new Foo()
on such a function. JS constructor semantics are a bit weird, so it might be simpler just to create objects using Foo()
, and have it return an object with an appropriate prototype.
It's pretty common to see people use HostObject
when they should instead just use a vanilla JS object, and add HostFunction properties to it. Depending on what you're trying to do with your C++ interop layer, this might be the best approach.
Another option is to use a small JS shim. Create a HostFunction FooHostFunc
, and JS like this:
function Foo() { return FooHostFunc.apply(this, arguments); };
var f = new Foo();
If you're trying to differentiate between Foo()
and new Foo()
in your HostFunction, that's also not going to be possible currently, but the uses cases for that are rare.
Thanks @mhorowitz, this approach works great. For reference, here is what I came up with based on your suggestion:
auto code = std::make_shared<const jsi::StringBuffer>("(function() { return TestClass.ctor.apply(this, arguments); })");
auto testClass = jsiRuntime.evaluateJavaScript(std::move(code), "some description").asObject(jsiRuntime);
testClass.setProperty(jsiRuntime, "ctor", jsi::Function::createFromHostFunction(jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "TestClass.ctor"), 1, [](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value
{
auto testObj = thisValue.asObject(runtime);
testObj.setProperty(runtime, "testProperty1", std::move(arguments[0]));
return nullptr;
}));
auto testClassPrototype = testClass.getProperty(jsiRuntime, "prototype").asObject(jsiRuntime);
testClassPrototype.setProperty(jsiRuntime, "testProperty2", 55);
testClassPrototype.setProperty(jsiRuntime, "testFunction", jsi::Function::createFromHostFunction(jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "testFunction"), 0, [](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value
{
thisValue.asObject(runtime).setProperty(runtime, "testProperty2", 66);
return jsi::Value(runtime, jsi::String::createFromAscii(runtime, "this is a test function"));
}));
jsiRuntime.global().setProperty(jsiRuntime, "TestClass", std::move(testClass));
Then in JavaScript I can do this:
const testObj = new TestClass(7);
console.log("TestClass.testProperty1: " + testObj.testProperty1); // prints 7
console.log("TestClass.testProperty2: " + testObj.testProperty2); // prints 55
console.log("TestClass.testFunction(): " + testObj.testFunction()); // prints 'this is a test function'
console.log("TestClass.testProperty2: " + testObj.testProperty2); // prints 66
Regarding this comment:
It's pretty common to see people use HostObject when they should instead just use a vanilla JS object, and add HostFunction properties to it.
Looking over jsi.h, I didn't understand the purpose of HostObject
for exactly the reason you state above - everything I wanted to do could be done by creating a jsi::Object
and just calling setProperty
on it (for values, or functions via HostFunction
). The only reason I could think to use HostObject
would be if you wanted the storage of the underlying properties to live on the C++ side, or if you wanted all property evaluation to be lazy. In what scenarios would you say HostObject
should be used?
That code looks about what I had in mind, glad you made it work.
A nit: evaluateJavaScript is pretty expensive, and it's possible (although not well documented) in resource-constrained environments to package Hermes without the parser/compiler at all, and just use bytecode. For prototyping, it's ok, but in a production environment, I'd include that stub in JS somewhere, and not emit one per "class". If you have many classes, you could write a single JS function like this:
function makeNativeClass(initializer) {
return function() { return initializer.apply(this, arguments); }
}
and then use it from C++:
// I haven't tested this, so consider it pseudocode
auto initializer = Function::createFromHostFunction(...);
runtime.global().setProperty(runtime, "TestClass",
runtime.global().getPropertyAsFunction(runtime, "makeNativeClass")
.call(runtime, initializer));
This avoids runtime calls into the compiler, and avoids per-class JS glue. If you did want to have the instance be a HostObject, then you'd need to change the pattern a bit to create one, but it sounds like you don't.
Which leads to your second question, those are the two clear cases: to associate native data with a JS object (although you can do this with a HostFunction, the pattern is different and less usable), and for lazy proprety evaluation. A subcase of this is when you don't know all the property names in advance.
Interesting, thanks for that extra info. Would it be safe to use jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "eval").call(jsiRuntime, code)
instead of jsiRuntime.evaluateJavaScript(code, "some description")
? I'm asking because we are doing this work in a lower level library below React Native where we don't have an obvious way to include JavaScript other than injecting it via an API like NAPI or JSI.
If you did want to have the instance be a HostObject, then you'd need to change the pattern a bit to create one
While I don't see a need at this point, I'm curious how we could make this work. My understanding is that when we use new
, JavaScript creates the object instance, so I'm not sure how you could make this pattern work when we want the object instance to be a HostObject
(which presumably needs to be instantiated by the C++ side of things). What would the pattern for this look like?
Would it be safe to use...
eval
eval also calls the parser/compiler, so there's not much of a difference. If you really have no other way, then you may not have a choice, and if the code is small, it's probably ok. You could also compile the JS code when building your library, include the bytecode as an asset, and then call evaluateJavaScript on that. That would avoid needing to run the compiler at runtime.
My understanding is that when we use
new
, JavaScript creates the object instance
As I mentioned, JS constructor semantics are a bit weird. You can see this in plain JS in any engine. JS will create an object which is used as the this
of the constructor. If the ctor doesn't return anything, this is the result of new
. If the ctor returns an object, new
returns that instead:
>> function C() { var o = Object.create({protoprop: 2}); o.prop = 3; return o; }
undefined
>> c = new C()
{ prop: 3 }
>> c.protoprop
2
So if C
returned a HostObject, then new C
would return it, too. You could do this by changing makeNativeClass to return the result from initializer
, for example.
Hi,
Is the new architecture [fabric] is ready to use shape? can any one answer?
We've been trying to use JSI to integrate Babylon Native into React Native but hit a roadblock when we found that the JSI JSCRuntime implementation doesn't support array buffers. The necessary JSC calls are commented out, even though the version of JSC currently used by React Native supports all these functions (JSValueGetTypedArrayType, JSObjectGetArrayBufferBytesPtr, and JSObjectGetArrayBufferByteLength). Are those comments (and associated commented out code) just out of date? Is there a plan/timeline to enable these functions?
Also, it looks like this (or something very similar) may have been under discussion fairly recently but that discussion seems to be awaiting input from @mhorowitz. cc @bghgary
@ryantrem Based on @tmikov's comment https://github.com/facebook/hermes/issues/182#issuecomment-583179072 I think if you submitted a PR to enable these functions and tested that they work, there's no reason not to accept that.
https://github.com/facebook/hermes/issues/182 is about adding additional functionality to JSI to make TypedArray easier to use and faster, which is related, but enabling ArrayBuffer in JSCRuntime could proceed in parallel.
Hello folks, I'd like to ask questions that will clarify my (and hopefully other people's too) mental model around JSI.
Switch
component which has its state in React / JS but the "true state" lives in UIKit. It's theoretically possible that the two states can get out of sync. RN handles it here using a combo of forceUpdate
and imperative calls in componentDidUpdate
. Will there be some way to remove this kind of state duplication? Is this part of the goal or is it non-goal? I guess in this case it's not really possible (?) but perhaps it could help in some cases in more complex components (TextInput comes to my mind).HostObject
is mentioned, is that the term as defined here or is that a JSI-specific term?Thanks a lot for answering!
Hi @vonovak. Thanks for asking!
HermesRuntime
implementation is in https://github.com/facebook/hermes/blob/master/API/hermes/hermes.cpp.HostObject
refers to this specific type. The name is originally inspired by the 2011 spec name. In modern spec terminology, a HostObject
provides the native implementation, via Object::createFromHostObject()
, of a particular kind of exotic object.
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
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)
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.