ABI 稳定性
🌐 ABI Stability
介绍
🌐 Introduction
应用二进制接口(ABI)是一种程序调用其他已编译程序的函数并使用其数据结构的方式。它是应用接口(API)的编译版本。换句话说,用于描述类、函数、数据结构、枚举和常量的头文件,使得应用能够执行所需任务,通过编译对应于一组地址、预期的参数值以及 ABI 提供者编译时使用的内存结构大小和布局。
🌐 An Application Binary Interface (ABI) is a way for programs to call functions and use data structures from other compiled programs. It is the compiled version of an Application Programming Interface (API). In other words, the headers files describing the classes, functions, data structures, enumerations, and constants which enable an application to perform a desired task correspond by way of compilation to a set of addresses and expected parameter values and memory structure sizes and layouts with which the provider of the ABI was compiled.
使用 ABI 的应用必须以这样的方式编译:可用地址、预期的参数值以及内存结构的大小和布局与 ABI 提供者的编译方式一致。通常,这可以通过使用 ABI 提供者提供的头文件进行编译来实现。
🌐 The application using the ABI must be compiled such that the available addresses, expected parameter values, and memory structure sizes and layouts agree with those with which the ABI provider was compiled. This is usually accomplished by compiling against the headers provided by the ABI provider.
由于 ABI 的提供者和 ABI 的使用者可能在不同时间使用不同版本的编译器进行编译,因此确保 ABI 兼容性的一部分责任在于编译器。不同版本的编译器,可能由不同厂商提供,都必须从具有特定内容的头文件生成相同的 ABI,并且必须为使用 ABI 的应用生成代码,使其根据 ABI 的约定访问给定头文件中描述的 API。现代编译器在不破坏它们所编译应用的 ABI 兼容性方面有相当不错的记录。
🌐 Since the provider of the ABI and the user of the ABI may be compiled at different times with different versions of the compiler, a portion of the responsibility for ensuring ABI compatibility lies with the compiler. Different versions of the compiler, perhaps provided by different vendors, must all produce the same ABI from a header file with a certain content, and must produce code for the application using the ABI that accesses the API described in a given header according to the conventions of the ABI resulting from the description in the header. Modern compilers have a fairly good track record of not breaking the ABI compatibility of the applications they compile.
确保 ABI 兼容性的剩余责任在于维护头文件的团队,这些头文件提供的 API 在编译后会生成需要保持稳定的 ABI。头文件可以进行修改,但必须密切跟踪修改的性质,以确保在编译后,ABI 不会发生更改,从而导致现有 ABI 用户与新版本不兼容。
🌐 The remaining responsibility for ensuring ABI compatibility lies with the team maintaining the header files which provide the API that results, upon compilation, in the ABI that is to remain stable. Changes to the header files can be made, but the nature of the changes has to be closely tracked to ensure that, upon compilation, the ABI does not change in a way that will render existing users of the ABI incompatible with the new version.
Node.js 中的 ABI 稳定性
🌐 ABI Stability in Node.js
Node.js 提供由多个独立团队维护的头文件。例如,像 node.h 和 node_buffer.h 这样的头文件由 Node.js 团队维护。v8.h 由 V8 团队维护,尽管与 Node.js 团队有密切合作,但它是独立的,有自己的时间表和优先级。因此,Node.js 团队只能部分控制项目提供的头文件中引入的更改。为此,Node.js 项目已经采用了语义化版本控制。这确保了项目提供的 API 在一个主版本内发布的所有次版本和补丁版本中都会产生稳定的 ABI。在实际操作中,这意味着 Node.js 项目承诺保证:针对特定主版本编译的 Node.js 原生插件,在该主版本的任何次版本或补丁版本中加载时都能成功运行。
🌐 Node.js provides header files maintained by several independent teams. For
example, header files such as node.h and node_buffer.h are maintained by
the Node.js team. v8.h is maintained by the V8 team, which, although in close
co-operation with the Node.js team, is independent, and with its own schedule
and priorities. Thus, the Node.js team has only partial control over the
changes that are introduced in the headers the project provides. As a result,
the Node.js project has adopted semantic versioning.
This ensures that the APIs provided by the project will result in a stable ABI
for all minor and patch versions of Node.js released within one major version.
In practice, this means that the Node.js project has committed itself to
ensuring that a Node.js native addon compiled against a given major version of
Node.js will load successfully when loaded by any Node.js minor or patch version
within the major version against which it was compiled.
N-API
已经出现了为 Node.js 提供一个 API 的需求,该 API 能够生成在多个 Node.js 主版本之间保持稳定的 ABI。创建这样一个 API 的动机如下:
🌐 Demand has arisen for equipping Node.js with an API that results in an ABI that remains stable across multiple Node.js major versions. The motivation for creating such an API is as follows:
- 自 JavaScript 语言诞生以来,它一直保持向后兼容,而执行 JavaScript 代码的引擎的 ABI 会随着 Node.js 的每一个大版本而变化。这意味着由完全用 JavaScript 编写的 Node.js 包组成的应用,在生产环境中引入新的 Node.js 大版本时,无需重新编译、重新安装或重新部署。相比之下,如果应用依赖于包含本地插件的包,那么每当生产环境中引入 Node.js 新的大版本时,应用就必须重新编译、重新安装并重新部署。包含本地插件的 Node.js 包与完全用 JavaScript 编写的包之间的这种差异,增加了依赖本地插件的生产系统的维护负担。
- 其他项目已经开始开发 JavaScript 接口,本质上是 Node.js 的替代实现。由于这些项目通常基于不同于 V8 的 JavaScript 引擎构建,它们的本地插件必然采用不同的结构并使用不同的 API。尽管如此,在不同实现的 Node.js JavaScript API 中使用同一个本地插件 API,将使这些项目能够利用围绕 Node.js 积累的 JavaScript 软件包生态系统。
- Node.js 将来可能包含不同的 JavaScript 引擎。这意味着,从外部来看,所有 Node.js 接口将保持不变,但 V8 头文件将不存在。如果 Node.js 不先提供一个与 JavaScript 引擎无关的 API 并被原生插件采用,这样的步骤将会整体上破坏 Node.js 生态系统,特别是原生插件的生态系统。
为此,Node.js 在 8.6.0 版本中引入了 N-API,并自 Node.js 8.12.0 起将其标记为项目的稳定组件。该 API 定义在头文件 node_api.h 和 node_api_types.h 中,并提供了跨 Node.js 主要版本的向前兼容性保证。该保证可以表述如下:
🌐 To these ends Node.js has introduced N-API in version 8.6.0 and marked it as a
stable component of the project as of Node.js 8.12.0. The API is defined in the
headers node_api.h and node_api_types.h, and provides a forward-
compatibility guarantee that crosses the Node.js major version boundary. The
guarantee can be stated as follows:
N-API 的指定版本 n 将在其发布的 Node.js 的主要版本中提供,并且在所有后续版本的 Node.js 中可用,包括后续的主要版本。
原生插件的作者可以利用 N-API 的前向兼容性保证,通过确保插件仅使用 node_api.h 中定义的 API 以及 node_api_types.h 中定义的数据结构和常量。这样做,作者可以促进他们的插件被采用,因为这向生产环境的用户表明,将原生插件添加到他们的项目中,增加的维护负担不会比添加一个纯 JavaScript 编写的包更多。
🌐 A native addon author can take advantage of the N-API forward compatibility
guarantee by ensuring that the addon makes use only of APIs defined in
node_api.h and data structures and constants defined in node_api_types.h.
By doing so, the author facilitates adoption of their addon by indicating to
production users that the maintenance burden for their application will increase
no more by the addition of the native addon to their project than it would by
the addition of a package written purely in JavaScript.
N-API 是有版本号的,因为新 API 会不时被添加。与语义版本不同,N-API 的版本号是累积的。也就是说,N-API 的每个版本的含义类似于语义版本系统中的次要版本,这意味着对 N-API 的所有更改都是向后兼容的。此外,新 N-API 会在实验标志下添加,以便社区有机会在生产环境中进行验证。实验状态意味着,尽管已经尽力确保新 API 未来不会以 ABI 不兼容的方式被修改,但它在生产环境中的可靠性和设计的正确性尚未得到充分验证,因此在最终纳入未来的 N-API 版本之前,它可能会经历 ABI 不兼容的更改。也就是说,实验性的 N-API 尚不在前向兼容保证范围内。
🌐 N-API is versioned because new APIs are added from time to time. Unlike semantic versioning, N-API versioning is cumulative. That is, each version of N-API conveys the same meaning as a minor version in the semver system, meaning that all changes made to N-API will be backwards compatible. Additionally, new N-APIs are added under an experimental flag to give the community an opportunity to vet them in a production environment. Experimental status means that, although care has been taken to ensure that the new API will not have to be modified in an ABI-incompatible way in the future, it has not yet been sufficiently proven in production to be correct and useful as designed and, as such, may undergo ABI-incompatible changes before it is finally incorporated into a forthcoming version of N-API. That is, an experimental N-API is not yet covered by the forward compatibility guarantee.