Skip to content

Stabilità dell'ABI

Introduzione

Un'Application Binary Interface (ABI) è un modo per i programmi di chiamare funzioni e utilizzare strutture dati da altri programmi compilati. È la versione compilata di un'Application Programming Interface (API). In altre parole, i file di intestazione che descrivono le classi, le funzioni, le strutture dati, le enumerazioni e le costanti che consentono a un'applicazione di eseguire un'attività desiderata corrispondono, tramite compilazione, a un insieme di indirizzi e valori di parametri attesi e dimensioni e layout delle strutture di memoria con cui è stato compilato il fornitore dell'ABI.

L'applicazione che utilizza l'ABI deve essere compilata in modo che gli indirizzi disponibili, i valori dei parametri attesi e le dimensioni e i layout delle strutture di memoria siano in accordo con quelli con cui è stato compilato il fornitore dell'ABI. Questo viene generalmente ottenuto compilando in base alle intestazioni fornite dal fornitore dell'ABI.

Poiché il fornitore dell'ABI e l'utente dell'ABI possono essere compilati in momenti diversi con versioni diverse del compilatore, una parte della responsabilità di garantire la compatibilità ABI risiede nel compilatore. Diverse versioni del compilatore, forse fornite da diversi fornitori, devono tutte produrre la stessa ABI da un file di intestazione con un certo contenuto e devono produrre codice per l'applicazione che utilizza l'ABI che accede all'API descritta in una data intestazione secondo le convenzioni dell'ABI risultante dalla descrizione nell'intestazione. I compilatori moderni hanno un buon track record nel non rompere la compatibilità ABI delle applicazioni che compilano.

La responsabilità rimanente per garantire la compatibilità ABI risiede nel team che gestisce i file di intestazione che forniscono l'API che risulta, dopo la compilazione, nell'ABI che deve rimanere stabile. È possibile apportare modifiche ai file di intestazione, ma la natura delle modifiche deve essere attentamente monitorata per garantire che, dopo la compilazione, l'ABI non cambi in un modo che renderà gli utenti esistenti dell'ABI incompatibili con la nuova versione.

Stabilità ABI in Node.js

Node.js fornisce file di intestazione gestiti da diversi team indipendenti. Ad esempio, i file di intestazione come node.h e node_buffer.h sono gestiti dal team Node.js. v8.h è gestito dal team V8, che, sebbene in stretta collaborazione con il team Node.js, è indipendente, con i propri tempi e priorità. Quindi, il team Node.js ha solo un controllo parziale sulle modifiche introdotte negli header forniti dal progetto. Di conseguenza, il progetto Node.js ha adottato il semantic versioning. Questo garantisce che le API fornite dal progetto si tradurranno in una ABI stabile per tutte le versioni minori e patch di Node.js rilasciate all'interno di una versione maggiore. In pratica, ciò significa che il progetto Node.js si è impegnato a garantire che un addon nativo di Node.js compilato contro una data versione maggiore di Node.js verrà caricato correttamente quando caricato da qualsiasi versione minore o patch di Node.js all'interno della versione maggiore contro cui è stato compilato.

N-API

È nata la necessità di dotare Node.js di un'API che si traduca in una ABI stabile tra più versioni maggiori di Node.js. La motivazione per la creazione di tale API è la seguente:

  • Il linguaggio JavaScript è rimasto compatibile con se stesso fin dai suoi primi giorni, mentre l'ABI del motore che esegue il codice JavaScript cambia con ogni versione maggiore di Node.js. Ciò significa che le applicazioni costituite da pacchetti Node.js scritti interamente in JavaScript non devono essere ricompilate, reinstallate o ridistribuite quando una nuova versione maggiore di Node.js viene inserita nell'ambiente di produzione in cui tali applicazioni vengono eseguite. Al contrario, se un'applicazione dipende da un pacchetto che contiene un addon nativo, l'applicazione deve essere ricompilata, reinstallata e ridistribuita ogni volta che viene introdotta una nuova versione maggiore di Node.js nell'ambiente di produzione. Questa disparità tra i pacchetti Node.js contenenti addon nativi e quelli scritti interamente in JavaScript ha aumentato il carico di manutenzione dei sistemi di produzione che si basano su addon nativi.

  • Altri progetti hanno iniziato a produrre interfacce JavaScript che sono essenzialmente implementazioni alternative di Node.js. Poiché questi progetti sono solitamente basati su un motore JavaScript diverso da V8, i loro addon nativi assumono necessariamente una struttura diversa e utilizzano un'API diversa. Tuttavia, l'utilizzo di una singola API per un addon nativo in diverse implementazioni dell'API JavaScript di Node.js consentirebbe a questi progetti di sfruttare l'ecosistema di pacchetti JavaScript che si è accumulato attorno a Node.js.

  • Node.js potrebbe contenere un motore JavaScript diverso in futuro. Ciò significa che, esternamente, tutte le interfacce Node.js rimarrebbero le stesse, ma il file di intestazione V8 sarebbe assente. Tale passo causerebbe la disruption dell'ecosistema Node.js in generale, e quello degli addon nativi in particolare, se non viene prima fornita da Node.js e adottata dagli addon nativi un'API agnostica del motore JavaScript.

A questi fini, Node.js ha introdotto N-API nella versione 8.6.0 e l'ha contrassegnata come componente stabile del progetto a partire da Node.js 8.12.0. L'API è definita negli header node_api.h e node_api_types.h e fornisce una garanzia di compatibilità in avanti che supera il limite della versione maggiore di Node.js. La garanzia può essere espressa come segue:

Una data versione n di N-API sarà disponibile nella versione maggiore di Node.js in cui è stata pubblicata e in tutte le versioni successive di Node.js, incluse le successive versioni maggiori.

Un autore di addon nativi può sfruttare la garanzia di compatibilità in avanti di N-API assicurandosi che l'addon utilizzi solo le API definite in node_api.h e le strutture dati e le costanti definite in node_api_types.h. In questo modo, l'autore facilita l'adozione del proprio addon indicando agli utenti di produzione che il carico di manutenzione della loro applicazione non aumenterà di più con l'aggiunta dell'addon nativo al loro progetto di quanto non lo farebbe con l'aggiunta di un pacchetto scritto puramente in JavaScript.

N-API è versione perché nuove API vengono aggiunte di volta in volta. A differenza del semantic versioning, il versioning di N-API è cumulativo. Cioè, ogni versione di N-API trasmette lo stesso significato di una versione minore nel sistema semver, il che significa che tutte le modifiche apportate a N-API saranno retrocompatibili. Inoltre, le nuove N-API vengono aggiunte sotto una bandiera sperimentale per dare alla community l'opportunità di verificarle in un ambiente di produzione. Lo stato sperimentale significa che, sebbene sia stata prestata attenzione per garantire che la nuova API non debba essere modificata in modo ABI-incompatibile in futuro, non è ancora stata sufficientemente provata in produzione per essere corretta e utile come progettato e, come tale, potrebbe subire modifiche ABI-incompatibili prima di essere finalmente incorporata in una prossima versione di N-API. Cioè, un'N-API sperimentale non è ancora coperta dalla garanzia di compatibilità in avanti.