Skip to content

[DRAFT] Add Node-API to Hermes #1377

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

Draft
wants to merge 1,622 commits into
base: static_h
Choose a base branch
from
Draft

Conversation

vmoroz
Copy link
Contributor

@vmoroz vmoroz commented Apr 18, 2024

Summary

This is an initial implementation of Node-API for Hermes.
The code is taken from the hermes-windows repo.

Node-API is an ABI-safe that is originally implemented for Node.js addons, and then adopted by all major JS runtimes.

This is a draft PR. Before removing the draft status I would like to ask a few questons:

  • What must be the library that exposes the Node-API? In hermes-windows we expose it for the hermes.dll. It is the libhermes for Windows. In this repo libhermes is not a shared library.
  • Please advise on the source file names and their locations.
  • What must be file headers?
  • I am going to add the test files after figuring out what must be the library that exposes Node-API.

Test Plan

Unit tests for Node-API are to be added.

@facebook-github-bot facebook-github-bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Apr 18, 2024
@tmikov
Copy link
Contributor

tmikov commented Apr 18, 2024

Thanks for doing this @vmoroz!!!

@facebook-github-bot
Copy link
Contributor

@tmikov has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@shirakaba
Copy link

Thanks so much, @vmoroz! Really excited to play with this.

@NathanWalker
Copy link

Huge effort @vmoroz thank you ❤️

@neildhar
Copy link
Contributor

Hey @vmoroz, thanks for putting this together!

Two high level requests:

  1. Could you move all the new code into the node-api directory? It can produce a static library that we link into libhermes, but I think keeping the implementation of this API separate makes more sense (similar to how the hermes_abi implementation is separate).
  2. As a first step, could we just include the implementations of the actual NAPI APIs? That is, we should exclude the extensions for providing things like CDP integration and access to Hermes specific functionality. We want to support these things eventually, but we haven't decided on what the ABI boundary should look like.

@vmoroz
Copy link
Contributor Author

vmoroz commented Jun 14, 2024

Hey @vmoroz, thanks for putting this together!

Two high level requests:

  1. Could you move all the new code into the node-api directory? It can produce a static library that we link into libhermes, but I think keeping the implementation of this API separate makes more sense (similar to how the hermes_abi implementation is separate).
  2. As a first step, could we just include the implementations of the actual NAPI APIs? That is, we should exclude the extensions for providing things like CDP integration and access to Hermes specific functionality. We want to support these things eventually, but we haven't decided on what the ABI boundary should look like.

@neildhar , thank you for the feedback and suggestions!
Extracting the Node API related code into the node-api folder similar to the hermes_abi code makes perfect sense. I will do that.

@wcandillon
Copy link

wcandillon commented Jul 11, 2024

Is this the main place to follow work on this topic? Is there still some appetite to support NAPI in React Native?
We are eyeing at some node modules that we would love to use out of the box in React Native.

@shirakaba
Copy link

shirakaba commented Jul 11, 2024

I'm still very interested in it and have even submitted conference talk proposals on the topic so would love it to become a reality!

I have some native Node addons that I've written for Electron, and it's a shame that I can use them in React Native Windows (as it supports Node-API) yet can't use them in any other flavour of React Native currently.

Let me know if there's any way I could help!

@vmoroz
Copy link
Contributor Author

vmoroz commented Jul 11, 2024

Is this the main place to follow work on this topic? Is there still some appetite to support NAPI in React Native? We are eyeing at some node modules that we would love to use out of the box in React Native.

One of the Discord RN channels or a discussion topic in one of the Meta's or Microsoft's related repo could be a better place to discuss all the details, but we can start it here.

The goal of supporting Node-API for Hermes, V8, and RN was to provide the industry standard ABI-safe API. It should enable versioning support for modules, support for multiple programming languages, and reuse of native code written for Node.js.

I wonder what is your scenario in terms of tergeting platforms (Windows, Mac, Android, etc.) and if you own the code that uses Node-API? It may be not possible to reuse binary Node.js modules, but reusing the majority of the code and recompiling it for the RN is a more achievable scenario.

@wcandillon
Copy link

wcandillon commented Jul 11, 2024 via email

@vmoroz
Copy link
Contributor Author

vmoroz commented Jul 11, 2024

We are implementing WebGPU for React Native ( https://x.com/appjsconf/status/1806332497783058900) and right now we are virtually migrating a webgpu node module to JSI manually. So of course the thought or being able to use that node module directly is appealing to us.

This is awesome! In the video clip you say that you target first of all the mobile platforms: iOS and Android.
It means that you cannot use the precompiled binaries and targeting the Node-API is mostly the matter of the convenience of reusing the existing code. IMHO, it is an achievable goal and @shirakaba's team is already using the Hermes Node-API for their project. The only problem is that to use it we must modify the Hermes code to integrate with the jsi::Runtime.
This PR targets to eliminate this issue and to enable Node-API out of the main Hermes repo.

The current version of the Node-API for Hermes is in the hermes-windows repo: https://github.com/microsoft/hermes-windows/ . There we are starting to factor out the Node-API code into its own folder.

Yesterday we had a long informative face-to-face meeting with Hermes team.
The Hermes team seems to be still in the favor of supporting Node-API. There was an interesting discussion about the interoperability between Node-API and jsi::Runtine/AbiRuntime. There are a few issues to address there. Hopefully we can do it soon.

@tmikov
Copy link
Contributor

tmikov commented Jul 12, 2024

@wcandillon when you say that you are implementing WebGPU, do you literally mean the WebGPU spec, 1 to 1, with shader language, etc?

@wcandillon
Copy link

wcandillon commented Jul 12, 2024

We use Google Dawn for the native WebGPU implementation (Dawn is also a supported Skia backend), we code generate most of the bindings from its TS definition (which is itself generated from the W3C spec, we also match TS methods to C++ methods, Dawn provides a json model for that), and for the manual part of the work we just look at the node module and migrate it. This is why being able to use that node binding directly (which is already conformant to the official WebGPU testsuite) feels appealing.

@tmikov
Copy link
Contributor

tmikov commented Jul 12, 2024

May I emphatically object to using Discord channels, as they inherently make discussions exclusionary, unfriendly to different timezones, transient, difficult to search, and impossible to reference.

r-barnes and others added 6 commits July 17, 2024 09:07
…ontEndDefs/Builtins.h +1

Summary:
`-Wduplicate-enum` identifies instances where enum categories are assigned the same value. When this is unintentional it is a bug.

This diff likely suppresses such an instance using clang diagnostic suppression or `FB_DUPLICATE_ENUM_OKAY`.

For questions/comments, contact r-barnes.

 - If you approve of this diff, please use the "Accept & Ship" button :-)

Reviewed By: palmje

Differential Revision: D59818696

fbshipit-source-id: 5561a1ff852f31b7d1da9d8689afd4f13eaa87c7
Summary: Release version 0.23.0

Reviewed By: gkz

Differential Revision: D59864810

fbshipit-source-id: ac8409d4a0706dbf9a2078bb4a3636f9eb53e8e9
Summary:
X-link: facebook/react-native#45507

Changelog: [Internal]

Reviewed By: SamChou19815

Differential Revision: D59891129

fbshipit-source-id: c1da0730d6c1b52cce5de730b47b3f3c854dff6b
Summary:
Make the CDP domain handler and corresponding tests gracefully handle
the case where Hermes is built without memory instrumentation.

Reviewed By: dannysu

Differential Revision: D59543260

fbshipit-source-id: 6f7fc28a240045b4e1dc441d55dd420c054eb41d
Summary: Mark local functions as static to fix some build configurations.

Reviewed By: dannysu

Differential Revision: D59561408

fbshipit-source-id: ee0a6dc6d2265ca88af6039fd2a387bd4dcbae1f
Summary:
Changelog [flow-api-translator]: Added support for `class A extends (Foo as Bar)` in the flow-api-translator

D58577282 added support for the legacy `TypeCastExpression`s, but did not add support for `AsExpression`s. Add support here.

Reviewed By: SamChou19815

Differential Revision: D59928177

fbshipit-source-id: 4a1d745428fc016da9dd7004aa8e26d223d09deb
@SamuelScheit
Copy link

For anyone who wants to try out this PR with react-native use version 0.75.0-rc.5 (or newer).

@SamuelScheit
Copy link

I was able to link and run a react-native app with this hermes NodeAPI PR.

However after calling hermes_create_napi_env it fails with EXC_BAD_ACCESS and the following stacktrace:

hermes::CompactArray::Fixed<unsigned char>::get(char*, unsigned int) [inlined]
hermes::CompactArray::get(unsigned int) const [inlined]
hermes::CompactTable::isEmpty(unsigned int) const [inlined]
unsigned int hermes::vm::detail::IdentifierHashTable::lookupString<char>(llvh::ArrayRef<char>, unsigned int, bool) consthermes_node_api/lib/VM/detail/IdentifierHashTable.cpp:41
hermes::vm::SymbolID hermes::vm::IdentifierTable::registerLazyIdentifierImpl<char>(llvh::ArrayRef<char>, unsigned int)hermes_node_api/lib/VM/IdentifierTable.cpp:393
hermes::napi::NapiEnvironment::NapiEnvironment(hermes::vm::Runtime&, bool, std::__1::shared_ptr<facebook::jsi::PreparedScriptStore>, hermes::vm::RuntimeConfig const&)Users/user/Developer/respond/hermes/hermes_node_api/API/hermes/hermes_napi.cpp:3330
hermes::napi::NapiEnvironment::NapiEnvironment(hermes::vm::Runtime&, bool, std::__1::shared_ptr<facebook::jsi::PreparedScriptStore>, hermes::vm::RuntimeConfig const&) [inlined]
::hermes_create_napi_env(void *, napi_env *)

raw is a NULL pointer and therefore results in EXC_BAD_ACCESS

static uint32_t get(char *raw, uint32_t idx) {
return reinterpret_cast<T *>(raw)[idx];
}

uint32_t get(uint32_t idx) const {
assert(idx < size_);
switch (scale_) {
case UINT8:
return Fixed<uint8_t>::get(raw_, idx);

bool isEmpty(uint32_t idx) const {
return CompactArray::get(idx) == EMPTY;
}

if (table_.isEmpty(idx)) {

auto idx = hashTable_.lookupString(str, hash);

https://github.com/vmoroz/hermes-windows/blob/5d4656887af7397700006e1beec28dc9abd3333f/API/hermes/hermes_napi.cpp#L3327-L3331

so what I'm currently trying to figure out is: why is raw a null pointer and what is the source of this issue

Does anybody else tried to use the NAPI in an app and if yes were you successful or did you encounter errors?

@SamuelScheit
Copy link

After building hermes in debug mode I get a different error, however without any stacktrace:

libc++abi: terminating due to uncaught exception of type std::length_error: vector

and after retrying a few times a different error:

libc++abi: terminating due to uncaught exception of type std::bad_alloc

Does anyone know how I can get the stacktrace of these errors to find the cause??

pieterv and others added 5 commits July 24, 2024 14:53
Summary:
If a program body has no statements, there is no need to use prettier to print it. Instead just return the docblock comment or an empty string.

This is needed due to an issue with prettier printing empty files with comments. With the hermes-transform infra today we perform comment attachment during "parse" so that comments are attached when transforming the AST. Typically however with prettier the attachment is done during printing, but this can be skipped by removing the `comments` array off the program node (so there are not comments to attach). This works great for normal nodes as it prints based off each nodes `comments` array. But for the docblock if there is no statement in the body to attach it to we need to add it to the program comments array but this triggers attachment which fails with our AST format.

Reviewed By: gkz

Differential Revision: D60191972

fbshipit-source-id: 31975a285e940af0ec0ce95363e8943a291996fb
Summary:
Dynamic cast requires ensuring that we generate RTTI for these records,
which means we need to keep more careful track of how they are defined.
Since we never need an actual dynamic cast, just replace it with
an asserting static cast.

Reviewed By: avp

Differential Revision: D60260766

fbshipit-source-id: bacce14fd5dee08b591ab7271b67feff0d3186cd
Reviewed By: alexmckenley

Differential Revision: D60294907

fbshipit-source-id: c7b4a83329140049a59f02f1a370306caad36d65
Summary:
Original Author: neildhar@meta.com
Original Git: 6b69a06
Original Reviewed By: avp
Original Revision: D59072005

The register allocator has the ability to honour a memory limit that is
proportional to the product of the number of instructions and basic
blocks in the function being allocated. Unfortunately, functions that
hit this limit by definition have a lot of instructions Even in the
most degenerate case where every block has one instruction, you need
4000 instructions to hit the 10M limit.

This diff tries to improve the quality of generated code in cases where
most values are used within the basic block they are defined in. In such
cases, we currently make the register available after the end of the
block. With this diff, the registers become available after their last
use in the block.

This is useful for functions with extremely large basic blocks, where
the current approach would end up allocating a huge number of registers
since the registers cannot be used within the same block.

Closes facebook#1448

Reviewed By: avp

Differential Revision: D60241766

fbshipit-source-id: 5196333862517cd546d675cf8fe005eb1ed5a790
Summary:
Move the stack accessors on Errors to the prototype. This makes them
easy to globally replace, which is useful for synth traces.

Reviewed By: avp

Differential Revision: D60323579

fbshipit-source-id: 283795a4dd5f4863b3a87b978da5991e48c1d92f
Summary:
X-link: facebook/react-native#50373

Changelog: [Internal]

Reviewed By: jbrown215, SamChou19815, panagosg7

Differential Revision: D72061660

fbshipit-source-id: a6862fe0e1f62992b76145006b3f8bc3ad788258
@kraenhansen
Copy link
Contributor

kraenhansen commented Mar 29, 2025

For anyone following along, I want to share a bit of context on the progress related to my previous comments on this PR. A group of developers (including @vmoroz, @mani3xis, @matthargett, @grabbou, @okwasniewski, @shirakaba, @ammarahm-ed and @teodorciuraru) has been persuing an effort to implement the runtime specific parts of Node-API for React Native, coordinated through the #react-native-node-api-modules channel on the Callstack discord and on the Callstack incubator's GitHub org: https://github.com/callstackincubator/react-native-node-api-modules

Many of us has had weekly meetings to collaborate: First drafting an RFC for React Native, later transforming it into a technical design and docs in the repository.

We choose to work without submitting a proposal for comments, mainly from the realization that very few (if any) changes was actually needed in React Native core, in a desire to keep the core lean.

In the short/mid term plan for this PR that we've agreed on:

  1. @vmoroz will rebase and force-push this PR branch on hermes-2025-03-03-RNv0.79.0-bc17d964d03743424823d7dd1a9f37633459c5c5
  2. @vmoroz will also push any outstanding commits that he feels are fairly stable
  3. We will then PRs against this PR branch (for the sake of transparency and to limit rework) and allow discussion on each proposed changes in isolation. To be clear those PRs will be created on his fork: https://github.com/vmoroz/hermes-windows/pulls
  4. As the PR of this branch gets closer to addressing the outstanding comments and todos, it will be moved out of draft.

@tmikov
Copy link
Contributor

tmikov commented Mar 29, 2025

@kraenhansen a couple of quick points:

  1. With the current design, simultaneous n-api and JSI usage in the same runtime is not entirely safe and can cause native crashes.
  2. Is there a goal to eventually land n-api in "mainline" Hermes? See Adding Node-API support to Static Hermes #1610. It makes more sense to target the "static_h" branch.

SamChou19815 and others added 3 commits March 30, 2025 11:01
Summary:
X-link: facebook/react-native#50387

Changelog: [Internal]

Reviewed By: gkz

Differential Revision: D72133412

fbshipit-source-id: f7719284db67537a2c652ea56d9f9050210da7d4
@vmoroz vmoroz force-pushed the PR/hermes_node_api branch from 5d46568 to 4780209 Compare March 30, 2025 19:27
@facebook-github-bot
Copy link
Contributor

@vmoroz has updated the pull request. You must reimport the pull request before landing.

@vmoroz vmoroz marked this pull request as draft March 30, 2025 19:28
@vmoroz
Copy link
Contributor Author

vmoroz commented Mar 30, 2025

@tmikov

  1. We are going to refactor the Node-API for Hermes code that it must be safe to use it along with the JSI and the ABI JSI code. The key is that the node_api instances must be created for an existing instance of Hermes runtime. We will remove all APIs that are not part of the standard Node-API. E.g. any APIs that are responsible for creating the Hermes runtime. I wonder what are the other unsafe patterns that you see.
  2. As for the requirements in Adding Node-API support to Static Hermes #1610: and the "static_h" branch:
    • I had moved the Node-API implementation into the separate folder: API/node_api_hermes to follow the same style as the other folders in the API folder.
    • Before we go out of the [DRAFT] mode we are going to add unit tests. IMHO, we must not merge this PR without unit tests. While we have unit tests for Node-API in hermes-windows, I believe we must improve them for this repo and this work is currently in progress.
    • Since it is not possible to change the PR target (at least I do not know how to do it), we will create another one for the "static_h" branch. I would propose to do it after we add tests and address all major outstanding TODOs.

@facebook-github-bot
Copy link
Contributor

@vmoroz has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@vmoroz has updated the pull request. You must reimport the pull request before landing.

@vmoroz vmoroz force-pushed the PR/hermes_node_api branch from 8092309 to dc09086 Compare April 10, 2025 13:42
@facebook-github-bot
Copy link
Contributor

@vmoroz has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@vmoroz has updated the pull request. You must reimport the pull request before landing.

@vmoroz vmoroz changed the base branch from main to static_h May 19, 2025 18:53
@neildhar
Copy link
Contributor

Hey @vmoroz, we're excited about the possibility of extending Hermes with NAPI and are eager to accept this PR! How close are you to having it ready for review?

@vmoroz
Copy link
Contributor Author

vmoroz commented May 30, 2025

Hey @neildhar, great to hear from you!
My main showstopper is the set of the unit tests, and I am so close to their completion.
My estimate is to be done with them in a couple of weeks or so.
They find some issues that must be fixed along the way.

It was very easy to re-target the PR for the static_h branch instead of main.
I will do the proper rebase as soon as the testing suite is ready.

@tmikov
Copy link
Contributor

tmikov commented May 30, 2025

For some additional context, we think n-api might be great for implementing builtin functionality like the new Temporal proposal, or even Intl, etc. In other words, we are eager to adopt it and use it ourselves for library functionality.

It should also make it dramatically easier for people to contribute library code to Hermes using a well known and stable API. (JSI is also an option for this, but it somewhat less suited because it relies on exceptions and RTTI, while Hermes itself is compiled without them.)

@vmoroz
Copy link
Contributor Author

vmoroz commented May 30, 2025

For some additional context, we think n-api might be great for implementing builtin functionality like the new Temporal proposal, or even Intl, etc. In other words, we are eager to adopt it and use it ourselves for library functionality.

It should also make it dramatically easier for people to contribute library code to Hermes using a well known and stable API. (JSI is also an option for this, but it somewhat less suited because it relies on exceptions and RTTI, while Hermes itself is compiled without them.)

Oh, wow! It sounds exciting. OK, It is going to be my top priority until I complete it then. Thank you! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Do not delete this pull request or issue due to inactivity.
Projects
None yet
Development

Successfully merging this pull request may close these issues.