ABI 的稳定性
简介
应用程序二进制接口(ABI)是程序通过调用函数使用其它编译程序中的数据结构的一种方式。它是应用程序接口(API)的编译版本。换句话说:描述类、函数、数据结构、枚举和常量的头文件,它们使应用程序能够执行所需的任务方法是编译到一组地址和预期的参数值和内存。编译 ABI 提供程序的结构大小和布局。
编译使用 ABI 的应用程序必须使得可用地址、预期参数值和内存结构大小和布局与 ABI 提供程序编译的那些协议一致。这通常是通过对 ABI 提供程序提供的标头进行编译来实现的。
由于 ABI 提供方和 ABI 用户可能会在不同时刻使用不同版本的编译器进行编译,因此确保 ABI 兼容性的部分责任在于编译器。不同版本的编译器(可能由不同的供应商提供)都必须从具有特定内容的头文件生成相同的 ABI,并且必须使用 ABI(根据约定使用给定头文件描述的 API)生成应用程序代码。现代编译器具有相当好的跟踪记录,不破坏它们编译的应用程序的 ABI 兼容性。
保持并确保 ABI 兼容性的责任在于维护标头文件的团队,后者提供 API;在编译后使得在 ABI 中保持稳定。对头文件进行更改是可以的,但必须密切跟踪更改的性质,以确保在编译时 ABI 不会更改,从而不会导致 ABI 的现有用户与新版本不兼容。
ABI 在 Node.js 中的稳定性
Node.js 提供的一些头文件是由几个独立的团队维护的。举个例子,诸如 node.h
和 node_buffer.h
是通过 Node.js 团队维护,而 v8.h
由 V8 团队维护,尽管它们之间的关系非常紧密,但是仍然都是独立的,并且都有自己的计划和优先级别。因此,Node.js 团队对于这些项目提供的头文件中引入的变更只有部分控制权。因此 Node.js 项目已采用语义版本控制。这可确保项目提供的 ABI 将为所有次要版本和修补程序版本的 Node.js 发布一个稳定的 ABI。在实践中,这将意味着 Node.js 项目已承诺确保:针对给定的主要版本的 Node.js 加载时,Node.js 本地化插件编译将成功加载由任何 Node.js 次要或修补程序版本在其编译的主要版本中。
N-API
为 Node.js 装备了一个 API 而导致在多个 Node.js 主要版本之间保持稳定的 ABI 的需求已经出现。创建此类 API 的动机如下:
JavaScript 语言自其早期就一直与自身兼容,而执行 JavaScript 代码的引擎 ABI 则随 Node.js 的每个主要版本而变化。这意味着在 JavaScript 中完全写入 Node.js 包中所包含的应用程序不需要重新编译和重新安装,或者作为新的主要版本的 Node.js 被丢弃到运行此类应用程序的生产环境中。相比之下,如果应用程序依赖于包含本机插件的包,仅将 Node.js 的新主版本引入到生产环境中就必须重新编译、重安装和部署应用程序。这种差距在包含本机插件的 Node.js 包和完全写入 JavaScript 的 Node.js 之间,增加了依赖于本机插件的生产系统的维护负担。
其他项目已经开始产出 JavaScript 接口,这基本上是 Node.js 的替代实现。因为这些项目通常是建立在不同 JavaScript 引擎而不是 V8;他们的本机插件必须采取不同的结构和使用不同的 API。不过,在 Node.js 的 JavaScript API 的不同实现中,使用本机插件的单个 API 将允许这些项目利用 Node.js 包中累积的 JavaScript 软件包的生态系统。
Node.js 可能在将来包含不同的 JavaScript 引擎。这意味着,对外所有 Node.js 接口将保持不变,但 V8 头文件将不存在。如果某个 API 没有首先被 Node.js 引擎提供,并且没有被本地插件采用。那么这一步将导致 Node.js 生态系统的中断——特别是本机插件的破坏。
对于这些收束,Node.js 已在版本 8.6.0 中引入了 N-API 并将其标记为项目的稳定组件——如 Node.js 8.12.0。这些 API 在头文件 node_api.h
和 node_api_types.h
中定义,并提供跨越 Node.js 主要版本边界的前向兼容性保证。保证可以说明如下:
N-API 的给定版本 N 将在其发布的 Node.js 的主要版本和所有后续版本的 Node.js 中提供,包括后续的主要版本。
本地插件开发者可以通过确保插件只使用 node_api.h
中定义的 API 以及在 node_api_types.h
中定义的数据结构和常量,利用 N-API 向前兼容性保证。由此方式,开发者通过向生产用户表明其应用程序的维护负担将增加,而不是通过添加一个纯粹由 JavaScript 编写的包,从而简化其插件的采用。
N-API 是版本化的,因为会不时添加新的 API;与语义版本控制不同,N-API 版本控制是累积性的。也就是说,N-API 的每个版本在 semver 系统中传达与次要版本相同的含义,这意味着对 N-API 所做的所有更改都将向后兼容。此外,新的 N-API 将添加到实验标志下,以使社区有机会在生产环境中对其进行审核。实验状态意味着,尽管已注意确保新 API 不会在将来以 ABI 不兼容的方式进行修改,但在生产中尚未充分证明它是正确和有用的。 因此,在最终并入即将推出的 N-API 版本之前可能会经历 ABI 不兼容的更改。也就是说,正向兼容性保证尚未涵盖实验性的 N-API。