资讯动态

Hermes NAPI 兼容性测试体系:引入 Node.js 官方测试套件并驱动其在本引擎上的落地验证

发布时间:2026/9/23 10:24:35 来源:尧图企业网站定制
Hermes NAPI 兼容性测试体系引入 Node.js 官方测试套件并驱动其在本引擎上的落地验证【免费下载链接】hermesA JavaScript engine optimized for running React Native.项目地址: https://gitcode.com/gh_mirrors/hermes/hermes本文以仓库 external/node-api-tests/VENDORED.md 为核心脉络完整解析 Hermes 如何将 Node.js v24.13.0 的官方 Node-APINAPI测试源码引入仓库构建出js-native-api与node-api两套测试目录并通过专用的测试运行器、补丁机制与 Skip List 在 Hermes 引擎上驱动这些测试。读完本文你将掌握这批 vendored 测试的来源、组成、取舍原则以及它们在 Hermes 中如何被编译为原生插件.nodeaddon、如何被调度执行、如何通过hermes-skip-list.txt维护已知差异从而理解 Hermes NAPI 实现API/napi 目录下的实现源码的兼容性验证全链路。一、背景为什么 Hermes 要引入 Node.js 的 NAPI 测试Node-API前身为 N-API是 Node.js 提供的一套稳定的 C/C 原生扩展接口它独立于 JavaScript 引擎的 V8 实现细节让同一份原生模块源码可以在不同引擎上编译运行。Hermes 作为专为 React Native 优化的 JavaScript 引擎在 API/napi 目录下实现了一套完整的 NAPI 接口头文件位于 include/hermes/napi。要实现与 Node.js 行为一致的 NAPI最直接的验证方式就是复用 Node.js 官方仓库中的测试套件。因此 Hermes 将 Node.js v24.13.0对应 commitdef0bdf8的部分 NAPI 测试源码 vendored引入并固化进仓库作为 Hermes NAPI 实现的兼容性测试基线详情见 external/node-api-tests/VENDORED.md。引入日期为 2026-02-10即该批次测试源固定于 Node.js v24.13.0 的def0bdf8提交。值得强调的是vendored 进来的不仅是测试用例本身还包括配套的头文件 include/hermes/napi/VENDORED.md 说明js_native_api.h、js_native_api_types.h、node_api.h、node_api_types.h四个头文件与 Node.jssrc/目录逐字节一致测试插件正是基于这批 vendored 头文件编译的这保证了测试的引擎无关属性——测的是 Hermes 对标准头文件声明的实现而非 Hermes 自定义的接口。二、测试目录结构与来源仓库中 vendored 测试的完整目录树如下external/node-api-tests/ ├── LICENSE # Node.js 项目许可MIT ├── VENDORED.md # 本文所述的引入说明 ├── js-native-api/ # 引擎无关的 Node-API 测试套件约 35 个套件 │ ├── 2_function_arguments/ │ ├── 3_callbacks/ │ ├── 4_object_factory/ │ ├── 5_function_factory/ │ ├── 6_object_wrap/ │ ├── 7_factory_wrap/ │ ├── 8_passing_wrapped/ │ ├── test_array/ │ ├── test_bigint/ │ ├── test_cannot_run_js/ │ ├── test_constructor/ │ ├── test_conversions/ │ ├── test_dataview/ │ ├── test_date/ │ ├── test_error/ │ ├── test_exception/ │ ├── test_finalizer/ │ ├── test_function/ │ ├── test_general/ │ ├── test_handle_scope/ │ ├── test_instance_data/ │ ├── test_new_target/ │ ├── test_number/ │ ├── test_object/ │ ├── test_promise/ │ ├── test_properties/ │ ├── test_reference/ │ ├── test_reference_double_free/ │ ├── test_sharedarraybuffer/ │ ├── test_string/ │ ├── test_symbol/ │ └── test_typedarray/ ├── node-api/ # 精选的 Node.js 专用测试套件 │ ├── 1_hello_world/ │ ├── test_buffer/ │ ├── test_exception/ │ ├── test_general/ │ ├── test_init_order/ │ ├── test_instance_data/ │ ├── test_null_init/ │ └── test_reference_by_node_api_version/ └── hermes/ # Hermes 侧测试运行器harness ├── CMakeLists.txt ├── README.md ├── assert.js ├── common.js ├── hermes-skip-list.txt ├── require.js └── run-node-tests.cmake每个测试套件目录下都包含三类文件C/C 插件源码如test_array.c、myobject.cc、binding.gypnode-gyp 构建描述Hermes 的 CMake 体系并不直接使用它而是按同样语义生成目标以及test.js驱动测试的 JS 脚本。以 js-native-api/2_function_arguments 为例2_function_arguments.c实现被导出为原生函数的 C 逻辑test.js从 JS 侧调用并断言行为。三、两类测试套件的定位差异3.1 js-native-api引擎无关的核心覆盖js-native-api/目录下是引擎无关的 Node-API 测试套件约 35 个它们直接测试js_native_api.h声明的核心函数理论上应当适用于任何符合规范的引擎。从目录名即可看出覆盖范围基础数据转换test_number、test_string、test_conversions、test_bigint、test_date、test_dataview、test_typedarray、test_array、test_sharedarraybuffer函数与对象模型2_function_arguments、3_callbacks、4_object_factory、5_function_factory、test_function、test_object、test_properties、test_new_target、test_constructor生命周期管理6_object_wrap、7_factory_wrap、8_passing_wrapped、test_reference、test_reference_double_free、test_finalizer异常与作用域test_error、test_exception、test_handle_scope、test_cannot_run_js运行时能力test_promise、test_symbol、test_general、test_instance_data这些套件验证的是 NAPI 的地基——任何宣称兼容 Node-API 的引擎都应当通过它们因此被完整引入。3.2 node-api针对非 Node 引擎的精选node-api/目录下则是经过挑选的 Node.js 专用测试套件仅纳入对非 Node 引擎依然有意义的部分。VENDORED.md 明确列出了纳入的 8 个套件套件目录测试关注点1_hello_world最小可用的插件注册与加载NAPI_MODULE宏test_buffernapi_create_buffer、外部 buffer 与 finalizertest_exception原生侧抛异常、异常状态清理test_general通用运行时行为、环境env交互test_init_order多个插件的初始化顺序test_instance_data环境实例数据napi_set_instance_data/napi_get_instance_datatest_null_initinit 函数为 NULL 时的注册行为test_reference_by_node_api_version按NAPI_VERSION区分引用语义排除的 16 个套件则全部与 Node.js 专属基础设施强耦合排除原因套件libuv 异步工作test_async、test_threadsafe_function、test_uv_loop、test_uv_threadpool_sizeNode.js 清理钩子test_async_cleanup_hook、test_cleanup_hookNode.js 异步上下文test_async_context、test_callback_scope环境拆除/GCtest_env_teardown_gcfatal 处理/崩溃测试test_fatal、test_fatal_exceptionmake_callback 机制test_make_callback、test_make_callback_recurseworker 线程test_worker_buffer_callback、test_worker_terminate、test_worker_terminate_finalization这一纳入/排除决策直接体现了测试策略只测引擎可移植的 API 语义不测 Node.js 专属的进程模型。Hermes 没有 libuv、没有 worker 线程基础设施这些套件即使强行运行也会因依赖缺失而失败排除它们让测试结果更能反映 NAPI 实现本身的正确性。四、测试运行器vendored 测试如何在 Hermes 上跑起来4.1 编译阶段把 C/C 源码编成 .node 插件测试的 C/C 源码并不能直接运行必须先编译成原生插件。这一步由 external/node-api-tests/hermes/CMakeLists.txt 中的add_node_test_addon()函数完成它接收TEST_CATEGORYjs-native-api或node-api、TEST_DIR_NAME与ADDON_NAME关键逻辑包括用add_library(... SHARED ...)为每个测试套件生成共享库目标命名形如node_test_js-native-api_test_array_test_array通过set_target_properties将产物命名为addon_name.nodeSUFFIX .node、PREFIX 并输出到构建树CMAKE_CURRENT_BINARY_DIR下的build/Release目录避免污染源码目录——这在使用 Hermes 作为 CMake 子项目时尤为重要引入三类头文件目录${PROJECT_SOURCE_DIR}/includeHermes 全局头、测试公共头目录common.h/common-inl.h/entry_point.h、以及${PROJECT_SOURCE_DIR}/include/hermes/napivendored 的 NAPI 头文件通过target_compile_definitions定义NODE_GYP_MODULE_NAME${ADDON_NAME}供entry_point.h中的NAPI_MODULE宏展开使用支持DEFINES关键字向特定插件追加编译定义典型用法是传NAPI_VERSION10或NAPI_VERSION9版本相关语义见后文在 Apple 平台上追加-undefined dynamic_lookup链接标志允许插件在运行时解析未定义符号。从 CMakeLists.txt 的调用点可以观察到一些重要细节6_object_wrap被拆成三个目标myobject、myobject_basic_finalizer、nested_wrap分别以不同的DEFINES编译同一批源码test_cannot_run_js被编译为两个目标分别以NAPI_VERSION10和NAPI_VERSION9验证不同版本下的行为差异test_reference_by_node_api_version同样以NAPI_VERSION10与NAPI_VERSION9编译两次。这正是 NAPI 版本分级的典型测试手段——NAPI 通过编译期宏NAPI_VERSION暴露不同层级的 APIHermes 自身实现与单元测试即使用NAPI_VERSION10见 API/napi/CMakeLists.txt 与 unittests/napi/CMakeLists.txt。同时部分测试套件因依赖 NAPI_EXPERIMENTAL 实验性 API如node_api_post_finalizer、napi_create_object_with_properties、node_api_is_sharedarraybuffer或uv.h而在当前 vendored 头文件下无法编译CMakeLists.txt 中对应行被注释并保留了原因说明这些套件会被跳过而非报错。4.2 运行阶段run-node-tests.cmake 的执行流水线编译完成后external/node-api-tests/hermes/run-node-tests.cmake 以cmake -P脚本模式执行全部测试其流水线为读取 harness 文件把assert.js、common.js、require.js的内容读入内存解析 Skip List逐行读取hermes-skip-list.txt去掉#注释与空白收集测试文件用file(GLOB_RECURSE ... *.js)在js-native-api/与node-api/下收集以test开头且非_hermes_前缀的 JS 文件并为每个文件打上类别标签组装合成脚本将var __testDir ...; var __addonDir ...;前缀 assert.jscommon.jsrequire.js 被测test.js拼接为一个临时文件写入构建树strict 模式检测用正则^([ \t]*(//[^\n]*)?\n)*[ \t]*[\]use strict[\]检测原始测试源码顶部跳过注释与空行是否带use strict指令若是则给 Hermes 传-strict标志执行以execute_process运行hermes可附加HERMES_ARGS如-gc-sanitize-handles1超时 120 秒若指定了QEMU_RUN_PREFIX如qemu-arm -L /path/to/sysroot则在其前缀下运行从而支持交叉编译产物在宿主机上执行统计与断言按退出码统计 Pass/Fail/Skip任何失败都会在汇总后以FATAL_ERROR终止构建保证 CI 能及时暴露回归。为什么需要require.js和common.js这两个 shimNode.js 测试依赖require()、assert模块、common辅助模块、gc全局与global别名等 Node 环境特性而 Hermes 没有这些。因此 harness 提供了最小化实现require.js、common.jsrequire.js实现globalThis.require内置映射assert、../../common、../common、../../common/gc等模块并识别./build/Release/xxx与./build/Debug/xxx形态的路径从__addonDir加载.node原生插件common.js则实现mustCall/mustNotCall/expectsError/platformTimeout等常用工具并把globalThis.global别名、global.gc()优先调用 Hermes 内建gc_()失败则退化为分配垃圾对象触发 GC补齐。由于这些前置代码会被拼接到测试脚本顶部use strict指令便不再处于脚本首行这正是第 5 步需要单独检测并转交-strict标志的原因。五、针对 Hermes 的兼容性补丁与 Skip List 维护5.1 错误消息正则补丁Hermes 与 V8 在严格模式 TypeError 的错误消息格式上存在差异external/node-api-tests/hermes/README.md 给出了两组典型对比V8: Cannot assign to read only property x of object #Object Hermes: Cannot assign to read-only property x V8: Cannot set property x of #Object which has only a getter Hermes: Cannot assign to property x which has only a getter因此测试里assert.throws()的匹配正则被放宽为同时接受两种格式但仍校验错误类型与属性名涉及三个文件js-native-api/test_properties/test.js、js-native-api/test_constructor/test.js、js-native-api/6_object_wrap/test.js。所有补丁均以[Hermes patch]注释标记便于后续同步上游测试时识别与处理。5.2 Skip List记录已知差异的白名单式管理hermes-skip-list.txt 是维护测试状态的核心文件支持五级匹配粒度dir_name # 跳过该目录下两个类别的全部测试 dir_name/test_file # 跳过指定测试文件两个类别 js-native-api/dir_name # 仅跳过 js-native-api 类别下该目录 js-native-api/dir/test # 仅跳过 js-native-api 类别下指定测试 node-api/dir_name # 仅跳过 node-api 类别下该目录 node-api/dir/test # 仅跳过 node-api 类别下指定测试运行器会按类别限定条目优先、未限定条目兜底的顺序匹配越具体的条目优先级越高。Skip List 的头部注释明确声明了它的演进原则本清单会随新 API 的实现而更新一旦所需 API 可用测试就应从清单中移除——即它不是永久豁免而是已知差异的追踪清单。从 Skip List 可以观察到 Hermes 当前 NAPI 实现的已知边界例如依赖 Node 专属模块test_general/testEnvCleanup、test_exception/testFinalizerException需要require(child_process)node-api/1_hello_world/test需要require(worker_threads)实验性 API 缺失test_sharedarraybuffernode_api_is_sharedarraybuffer、test_objectnapi_create_object_with_properties、test_finalizernode_api_post_finalizer对应的插件无法编译Hermes 实现语义差异test_array不支持 2^32-1 大小的数组、test_stringHermes 总是拷贝外部字符串copiedtrue而测试期望未被拷贝、test_typedarray0 长度外部 ArrayBuffer 在 Hermes 中被视为 attached测试期望 detached、test_reference/testwrap finalizer 内对同一对象的弱引用调用napi_get_reference_value失败GC 期间限制test_cannot_run_jsfinalizer 在 GC 期间执行时不可运行 JS。这些条目为后续实现工作提供了精确的待办清单也让测试结果始终反映可验证的兼容性而非被失败掩盖的兼容性。六、构建与运行方式要运行这批测试需要先启用 NAPI 支持再触发构建目标。综合 external/CMakeLists.txt 与 external/node-api-tests/hermes/CMakeLists.txt 的定义其接入关系为HERMES_ENABLE_NAPION └─ external/CMakeLists.txt 加载 node-api-tests/hermes └─ add_custom_target(check-napi-node-tests ...) # 只跑 Node.js 来源测试 add_custom_target(check-napi ...) # 汇总目标CTS Node.js 测试check-napi-node-tests依赖hermes可执行文件与全部node_test_*插件目标随后以cmake -P run-node-tests.cmake驱动整个测试流水线check-napi由 external/CMakeLists.txt 定义的汇总目标同时依赖check-napi-ctsNode-API Conformance Test Suite见external/node-api-cts/implementors/hermes与check-napi-node-tests一条命令即可跑完全部 NAPI 兼容性验证。配置阶段的关键开关是HERMES_ENABLE_NAPI。交叉编译场景下可通过QEMU_RUN_PREFIX变量传入 qemu 前缀使交叉编译出的 Hermes 在宿主机上执行测试。七、许可证与上游同步根据 VENDORED.md 的说明vendored 文件来自 Node.js 项目遵循 Node.js 许可证MIT许可证文本随附于 external/node-api-tests/LICENSE。这意味着这批测试可以随 Hermes 仓库一并分发不引入额外许可负担。上游同步的机制同样清晰本次 vendored 的版本锚定在 Node.js v24.13.0commitdef0bdf8include/hermes/napi/VENDORED.md 同时给出了头文件的刷新指引——用目标 Node.js 发行版中的同名文件替换include/hermes/napi下四个头文件并更新版本表即可。相应地测试源码侧的更新则需要结合 external/node-api-tests/hermes/README.md 中记录的补丁清单[Hermes patch]标记逐一复核避免上游改动覆盖本地适配。八、小结一套可演进的兼容性验证闭环回顾整套体系Hermes 对 Node.js NAPI 测试的引入并非简单的拷贝粘贴而是一个完整的验证闭环来源可追溯版本v24.13.0 /def0bdf8、日期2026-02-10、许可MIT在 VENDORED.md 中全部记录范围有取舍引擎无关的js-native-api全量纳入Node 专属的node-api精选 8 个套件16 个依赖 libuv/worker/Node 进程模型的套件被明确排除运行有保障CMake 构建插件 cmake -P运行器 require.js/common.jsshim strict 检测 QEMU 交叉支持让上游测试真正在 Hermes 上可执行差异有追踪错误消息正则补丁和 Skip List 让已知差异透明化并明确要求在新 API 落地后移除豁免项。对读者而言若你想为 Hermes 的 NAPI 实现做贡献hermes-skip-list.txt 就是最直接的入手点——移除一条豁免、让对应测试从 Skip 变为 Pass即是真实、可验证、可合入的改进。【免费下载链接】hermesA JavaScript engine optimized for running React Native.项目地址: https://gitcode.com/gh_mirrors/hermes/hermes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价