Drop the host's shadowing implementations of runtime Node-API functions - #434
Merged
Merged
Conversation
kraenhansen
pushed a commit
that referenced
this pull request
Aug 13, 2026
The label table and example condition used plain "host", copied from .github/workflows/check.yml's host-cpp-tests job at the time. That job's condition was itself wrong — the repository's real label is "Host 🏡" (see issues #428/#420/#412) — confirmed and fixed on #434's branch. Update this doc to match, and note that a label condition needs to be checked against the real, existing label rather than trusted at face value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm
Collaborator
Author
|
Swapped the 🤖 Generated with Claude Code https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm Generated by Claude Code |
kraenhansen
added a commit
that referenced
this pull request
Aug 13, 2026
* docs: instruct Claude Code to attach CI labels when opening PRs .github/workflows/check.yml's pull_request trigger fires only on opened, synchronize and reopened — not labeled. Several jobs are gated on labels being present in that triggering event's payload (host, Apple 🍎, Android 🤖, MacOS 💻, Ferric 🦀, weak-node-api), so a label attached after PR creation (the only way the create_pull_request MCP tool allows, since it has no labels parameter) never actually triggers those jobs without a follow-up push. Adds .claude/CLAUDE.md documenting the label -> job mapping and the attach-then-push-again workflow, prompted by PR #434 needing labels added and retriggered after the fact. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm * docs: correct the host label name to "Host 🏡" The label table and example condition used plain "host", copied from .github/workflows/check.yml's host-cpp-tests job at the time. That job's condition was itself wrong — the repository's real label is "Host 🏡" (see issues #428/#420/#412) — confirmed and fixed on #434's branch. Update this doc to match, and note that a label condition needs to be checked against the real, existing label rather than trusted at face value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm --------- Co-authored-by: Claude <noreply@anthropic.com>
Hermes' first-party Node-API (adopted in #372, integrated via hermes_napi_host in #398) already implements the buffer functions, napi_get_version and napi_get_node_version. RuntimeNodeApi.{cpp,hpp} still defined all of these, and since the generated injector (scripts/generate-injector.mts) resolves each NodeApiHost field by unqualified name inside `namespace callstack::react_native_node_api`, the host's shims won and Hermes' implementations were never reached. Remove napi_create_buffer, napi_create_buffer_copy, napi_create_external_buffer, napi_get_buffer_info, napi_is_buffer, napi_get_version and napi_get_node_version from RuntimeNodeApi.{cpp,hpp}, letting unqualified lookup fall through to Hermes' own symbols. This also fixes two bugs the shims carried: - napi_get_buffer_info wrote its typed-array-kind output into a mutable global (`ArrayType`) that every subsequent napi_create_buffer / napi_create_external_buffer call read back, so calling it on e.g. a Float64Array corrupted every later buffer creation (and raced across runtimes). - napi_create_buffer_copy accepted `result_data` but never wrote it. napi_is_buffer / napi_get_buffer_info also become stricter, matching Node: true/napi_ok only for Uint8Array, napi_invalid_arg otherwise, instead of accepting any ArrayBuffer/TypedArray. Keep napi_fatal_error's host-side implementation: Hermes routes it to stderr, which is not logcat on Android, while the host's version reaches logcat via the "NodeApiHost" logger tag. Documented why this one intentionally keeps shadowing Hermes so a future sweep doesn't remove it as dead weight. RuntimeNodeApi.{cpp,hpp} keep their own translation unit rather than folding into Logger-adjacent code: the file now holds exactly the one shim the host deliberately keeps, and renaming would touch the injector, CMakeLists and podspec globbing for no functional benefit. Adds a changeset (patch) for the observable behavior change: addons now see Hermes' real napi_get_node_version instead of napi_generic_failure, and the stricter buffer type-checking. Closes #67. Verified: pnpm install && pnpm run build, pnpm --filter react-native-node-api run test (pre-existing failures only, confirmed present on unmodified origin/next too - they stem from running as root, not this change), eslint and prettier on touched files. Native C++ compilation was not verified - no Android/iOS toolchain is available in this environment. Closes #428 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm
The job checked for a label literally named "host", but the repository's real label (used on issues, e.g. #428/#420/#412) is "Host 🏡" — a label named plain "host" existed too, seemingly a leftover/duplicate, and has since been deleted. The condition never actually matched the label anyone would apply in practice, so this job only ever ran on pushes to main/next, never on a labeled PR. Found while attaching labels to this PR: the CI still showed green with host-cpp-tests silently not running, exactly the kind of gap .claude/CLAUDE.md (#436) exists to prevent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm
kraenhansen
force-pushed
the
claude/issue-428-drop-shadowing-napi-shims
branch
from
August 13, 2026 10:33
0317817 to
3a7323b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hermes' first-party Node-API (adopted in #372, wired up via
hermes_napi_hostin #398) already implements the entire runtime surface, including buffers,napi_fatal_error,napi_get_versionandnapi_get_node_version. Butpackages/host/cpp/RuntimeNodeApi.{cpp,hpp}still defined eight of these — and won, because the generated injector (packages/host/scripts/generate-injector.mts) resolves eachNodeApiHostfield by unqualified name insidenamespace callstack::react_native_node_api, andRuntimeNodeApi.hppis included there. So unqualified lookup found the host's shim before it ever reached Hermes' exported symbol.This PR removes seven of those eight functions, letting the unqualified-name lookup fall through to Hermes' own implementations (the same mechanism every other Node-API function in this codebase already relies on —
RuntimeNodeApi.cppitself callsnapi_create_arraybuffer,napi_typeof, etc. without defining them, and those already resolve to Hermes today):napi_create_buffer,napi_create_buffer_copy,napi_create_external_buffer,napi_get_buffer_info,napi_is_buffer— dropped.napi_get_version— dropped (Hermes' answer is identical,NAPI_VERSION).napi_get_node_version— dropped. Closes Implementnapi_get_node_version#67: addons now get Hermes' actual version instead ofnapi_generic_failure.napi_fatal_error— kept, with a comment on the declaration explaining why: Hermes routes it to stderr, which isn't logcat on Android, while the host's version reaches logcat via theNodeApiHostlogger tag.This also removes two bugs the shims carried:
napi_get_buffer_infooverwrote a mutable global (ArrayType) used bynapi_create_buffer/napi_create_external_buffer; calling it on a non-Uint8Arraytyped array (e.g.Float64Array) corrupted every later buffer creation on that process, and raced across runtimes.napi_create_buffer_copyacceptedresult_databut never wrote it.napi_is_buffer/napi_get_buffer_infoalso become stricter, matching Node:true/napi_okonly forUint8Array,napi_invalid_argotherwise (previously anyArrayBuffer/TypedArray). This is an intentional, documented behavior change (see the changeset), and also tightens #171.RuntimeNodeApi.{cpp,hpp}keep their own translation unit rather than folding intoLogger-adjacent code — the file now holds exactly the one shim the host deliberately keeps shadowing, and renaming would touch the generated injector,CMakeLists.txt, and the podspec's glob for no functional benefit.packages/host/android/CMakeLists.txtneeded no changes as a result.A changeset (
patch) documents the observable behavior change fornapi_get_node_versionand the buffer functions.Why this is safe (reachability of Hermes' symbols)
I verified, source-level, that removing the host's declarations doesn't strand these calls:
NodeApiHost { .name = name, ... }entries are produced for every Node-API function fromnode-api-headers(packages/weak-node-api/src/node-api-functions.ts), regardless of whether the host defines it — this list is unaffected by this change.RuntimeNodeApi.cppalready calls many Node-API functions (napi_create_arraybuffer,napi_typeof,napi_is_arraybuffer,napi_get_arraybuffer_info,napi_get_typedarray_info,napi_create_external_arraybuffer) without defining them anywhere in this repo — those already resolve, at link time, to Hermes' exported symbols today. OnceRuntimeNodeApi.hppno longer declares the seven removed functions insidenamespace callstack::react_native_node_api, unqualified lookup for them falls through to the same global-namespace declarations fromnode_api.h(already used identically for the calls above) and links against Hermes'hermesvm/xcframework the same way.Verified
pnpm install && pnpm run build— passes.pnpm --filter react-native-node-api run test— passes except three pre-existing, unrelated failures inpath-utils.test.tsaround unreadable-file/permission handling; confirmed those fail identically on unmodifiedorigin/nextin this sandbox (it runs asroot, which bypasses the permission checks the tests rely on).pnpm exec eslint packages/hostandpnpm exec prettier --checkon touched files — clean.RuntimeNodeApisymbols outside the two edited files.Not verified (needs a real device/toolchain)
packages/node-addon-examples/tests/buffersstill passing on device — reviewed the test source (addon.c/addon.js) against both the old and new (Hermes) semantics; every buffer the test creates goes throughnapi_create_buffer/napi_create_external_buffer(producing realUint8Arrays under both implementations), and its one non-buffer check (invalidObjectAsBuffer) passesNULLfor bothdata/length, which tripsnapi_invalid_argunder either implementation. So this should still pass, but this checklist item from Drop the host's shadowing implementations of runtime Node-API functions #428 remains pending human/CI verification on an actual device.Closes #428, closes #67.
🤖 Generated with Claude Code
https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm
Generated by Claude Code