ABI 穩定性
介紹
應用程式二進位介面(ABI)是程序從其他已編譯程式中呼叫函數並使用資料結構的方法。它是應用程式介面(API)的已編譯版本。換句話說,描述類別、函數、資料結構、列舉和常數的標頭檔案,使應用程式能夠執行所需任務,透過編譯對應到提供 ABI 的一組位址、預期參數值和記憶體結構大小和佈局。
使用 ABI 的應用程式必須經過編譯,使可用的位址、預期參數值和記憶體結構大小和佈局與 ABI 提供者編譯時一致。通常通過對 ABI 提供者提供的標頭檔案進行編譯來完成此操作。
由於 ABI 的提供者和 ABI 的使用者可能在不同時間使用不同版本的編譯器進行編譯,確保 ABI 兼容性的一部分責任歸於編譯器。不同版本的編譯器,可能由不同供應商提供,必須從具有特定內容的標頭檔案產生相同的 ABI,並且必須為使用 ABI 的應用程式產生代碼,該代碼根據標頭中描述的 API 訪問 ABI 的規則進行訪問。現代編譯器在不破壞其編譯的應用程式的 ABI 兼容性方面,具有相當良好的記錄。
確保 ABI 兼容性的其餘責任歸於維護提供 API 的標頭檔案的團隊,經過編譯後產生的 ABI 必須保持穩定。可以進行標頭檔案的更改,但必須密切追蹤更改的性質,以確保在編譯後,ABI 不會以使得現有的 ABI 使用者與新版本不兼容的方式發生變化。
Node.js 中的 ABI 穩定性
Node.js 提供由多個獨立團隊維護的標頭文件。例如,由 Node.js 團隊維護的標頭文件如 node.h
和 node_buffer.h
。而 v8.h
則由 V8 團隊維護,雖然與 Node.js 團隊密切合作,但是它是獨立的,擁有自己的時間表和優先順序。因此,Node.js 團隊僅對專案提供的標頭中引入的更改具有部分控制權。因此,Node.js 專案已採用 語義化版本。這確保專案提供的 API 將導致在一個主要版本內釋出的 Node.js 的所有次要和補丁版本中的穩定 ABI。在實踐中,這意味著 Node.js 專案已承諾確保針對給定的 Node.js 主要版本編譯的 Node.js 原生插件將在由其編譯的主要版本中的任何 Node.js 次要版本或補丁版本加載時成功加載。
N-API
Node.js 的需求已經出現,需要配備一個 API,該 API 導致 ABI 在多個 Node.js 主要版本之間保持穩定。創建此類 API 的動機如下所述
-
自 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 主要版本邊界的向前兼容性保證。這個保證可以陳述如下:
N-API 的特定版本 n 將在其發佈的 Node.js 主要版本中可用,並在所有後續版本中可用,包括後續的主要版本。
本地插件作者可以利用 N-API 的向前兼容性保證,確保插件僅使用在 node_api.h
中定義的 API,以及在 node_api_types.h
中定義的數據結構和常量。這樣一來,作者可以通知生產用戶,將本地插件添加到他們的項目中所增加的維護負擔,與將一個純粹由 JavaScript 編寫的套件添加到項目中所增加的維護負擔相比,不會增加更多。
N-API 是有版本的,因為不時會添加新的 API。與語義版本不同,N-API 版本是累積的。也就是說,每個 N-API 版本傳達的含義與語義版本系統中的次要版本相同,這意味著對 N-API 所做的所有更改都是向後兼容的。此外,新的 N-API 會在實驗標誌下添加,以讓社區有機會在生產環境中進行驗證。實驗性質意味著盡管已經小心確保新的 API 在未來不會以不相容 ABI 的方式進行修改,但在生產中尚未充分證明其設計的正確性和有用性,因此在最終納入 N-API 的即將到來的版本之前,可能會經歷不相容 ABI 的更改。也就是說,實驗性質的 N-API 尚未受到向前兼容性保證的影響。