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 的头文件的团队,这些 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.hnode_buffer.h 之类的头文件由 Node.js 团队维护。v8.h 由 V8 团队维护,尽管与 Node.js 团队密切合作,但它是独立的,有自己的时间表和优先级。因此,Node.js 团队只能部分控制项目提供的标头中引入的更改。因此,Node.js 项目采用了 语义版本控制。这确保项目提供的 API 将为在一个主要版本内发布的所有次要和修补版本的 Node.js 产生稳定的 ABI。实际上,这意味着 Node.js 项目已保证确保针对给定主要版本的 Node.js 编译的 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 可使 ABI 在多个 Node.js 主要版本中保持稳定。创建此类 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 的每个主要版本而变化。这意味着,当 Node.js 的新主要版本被放入运行此类应用的生产环境中时,完全用 JavaScript 编写的 Node.js 包组成的应用无需重新编译、重新安装或重新部署。相反,如果应用依赖于包含原生插件的包,则每当将新的 Node.js 主要版本引入生产环境时,都必须重新编译、重新安装和重新部署该应用。包含原生插件的 Node.js 包与完全用 JavaScript 编写的包之间的这种差异增加了依赖原生插件的生产系统的维护负担。

    ¥The JavaScript language has remained compatible with itself since its very early days, whereas the ABI of the engine executing the JavaScript code changes with every major version of Node.js. This means that applications consisting of Node.js packages written entirely in JavaScript need not be recompiled, reinstalled, or redeployed as a new major version of Node.js is dropped into the production environment in which such applications run. In contrast, if an application depends on a package that contains a native addon, the application has to be recompiled, reinstalled, and redeployed whenever a new major version of Node.js is introduced into the production environment. This disparity between Node.js packages containing native addons and those that are written entirely in JavaScript has added to the maintenance burden of production systems which rely on native addons.

  • 其他项目已开始生成 JavaScript 接口,这些接口本质上是 Node.js 的替代实现。由于这些项目通常基于与 V8 不同的 JavaScript 引擎构建,因此它们的原生插件必然采用不同的结构并使用不同的 API。尽管如此,在 Node.js JavaScript API 的不同实现中使用单个 API 作为原生插件将允许这些项目利用围绕 Node.js 积累的 JavaScript 包生态系统。

    ¥Other projects have started to produce JavaScript interfaces that are essentially alternative implementations of Node.js. Since these projects are usually built on a different JavaScript engine than V8, their native addons necessarily take on a different structure and use a different API. Nevertheless, using a single API for a native addon across different implementations of the Node.js JavaScript API would allow these projects to take advantage of the ecosystem of JavaScript packages that has accrued around Node.js.

  • Node.js 将来可能会包含不同的 JavaScript 引擎。这意味着,从外部来看,所有 Node.js 接口都将保持不变,但 V8 头文件将不存在。如果 Node.js 没有首先提供与 JavaScript 引擎无关的 API 并被原生插件采用,则此步骤将导致 Node.js 生态系统的破坏,尤其是原生插件的破坏。

    ¥Node.js may contain a different JavaScript engine in the future. This means that, externally, all Node.js interfaces would remain the same, but the V8 header file would be absent. Such a step would cause the disruption of the Node.js ecosystem in general, and that of the native addons in particular, if an API that is JavaScript engine agnostic is not first provided by Node.js and adopted by native addons.

为此,Node.js 在 8.6.0 版本中引入了 N-API,并从 Node.js 8.12.0 开始将其标记为项目的稳定组件。API 在标头 node_api.hnode_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 将在其发布的 Node.js 主要版本以及 Node.js 的所有后续版本(包括后续主要版本)中可用。

¥A given version n of N-API will be available in the major version of Node.js in which it was published, and in all subsequent versions of Node.js, including subsequent major versions.

原生插件作者可以利用 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 都与 semver 系统中的次要版本具有相同的含义,这意味着对 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.