ABI Стабильность
Введение
Application Binary Interface (ABI) — это способ для программ вызывать функции и использовать структуры данных из других скомпилированных программ. Это скомпилированная версия Application Programming Interface (API). Другими словами, заголовочные файлы, описывающие классы, функции, структуры данных, перечисления и константы, которые позволяют приложению выполнять желаемую задачу, посредством компиляции соответствуют набору адресов и ожидаемых значений параметров, а также размерам и компоновке структуры памяти, с которыми был скомпилирован поставщик ABI.
Приложение, использующее ABI, должно быть скомпилировано таким образом, чтобы доступные адреса, ожидаемые значения параметров, размеры и компоновка структуры памяти соответствовали тем, с которыми был скомпилирован поставщик ABI. Обычно это достигается путем компиляции с использованием заголовочных файлов, предоставленных поставщиком ABI.
Поскольку поставщик ABI и пользователь ABI могут быть скомпилированы в разное время с разными версиями компилятора, часть ответственности за обеспечение совместимости ABI лежит на компиляторе. Разные версии компилятора, возможно, предоставленные разными поставщиками, должны создавать один и тот же ABI из заголовочного файла с определенным содержанием, и должны создавать код для приложения, использующего ABI, который обращается к API, описанному в данном заголовке, в соответствии с соглашениями ABI, полученными из описания в заголовке. Современные компиляторы имеют довольно хорошие показатели по не нарушению ABI-совместимости скомпилированных ими приложений.
Остальная ответственность за обеспечение ABI-совместимости лежит на команде, поддерживающей заголовочные файлы, которые предоставляют API, который в результате компиляции приводит к ABI, который должен оставаться стабильным. В заголовочные файлы можно вносить изменения, но характер изменений необходимо тщательно отслеживать, чтобы гарантировать, что после компиляции ABI не изменится таким образом, что существующие пользователи 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, скомпилированный для определенной мажорной версии Node.js, успешно загружался при загрузке любой минорной или патч-версией Node.js в пределах мажорной версии, для которой он был скомпилирован.
N-API
Возник спрос на оснащение Node.js API, который обеспечивал бы ABI, остающийся стабильным для нескольких мажорных версий Node.js. Мотивация для создания такого API заключается в следующем:
Язык JavaScript оставался совместимым с самим собой с самых первых дней, тогда как ABI движка, выполняющего код JavaScript, меняется с каждой мажорной версией Node.js. Это означает, что приложения, состоящие из пакетов Node.js, написанных полностью на JavaScript, не нужно перекомпилировать, переустанавливать или повторно развертывать, когда новая мажорная версия Node.js попадает в производственную среду, в которой работают такие приложения. В отличие от этого, если приложение зависит от пакета, содержащего нативный аддон, приложение должно быть перекомпилировано, переустановлено и повторно развернуто всякий раз, когда в производственную среду вводится новая мажорная версия Node.js. Этот разрыв между пакетами Node.js, содержащими нативные аддоны, и пакетами, написанными полностью на JavaScript, увеличил нагрузку на обслуживание производственных систем, которые полагаются на нативные аддоны.
Другие проекты начали создавать JavaScript-интерфейсы, которые по сути являются альтернативными реализациями Node.js. Поскольку эти проекты обычно построены на другом JavaScript-движке, чем V8, их нативные аддоны обязательно имеют другую структуру и используют другой API. Тем не менее, использование единого API для нативного аддона в разных реализациях JavaScript API Node.js позволило бы этим проектам воспользоваться преимуществами экосистемы JavaScript-пакетов, которая накопилась вокруг Node.js.
Node.js может содержать другой JavaScript-движок в будущем. Это означает, что внешне все интерфейсы Node.js останутся прежними, но заголовочный файл V8 будет отсутствовать. Такой шаг приведет к нарушению экосистемы Node.js в целом и экосистемы нативных аддонов в частности, если Node.js сначала не предоставит API, не зависящий от JavaScript-движка, и не будет принят нативными аддонами.
В этих целях Node.js представил N-API в версии 8.6.0 и отметил его как стабильный компонент проекта начиная с Node.js 8.12.0. API определен в заголовках node_api.h
и node_api_types.h
и предоставляет гарантию прямой совместимости, которая пересекает границу мажорной версии Node.js. Гарантию можно сформулировать следующим образом:
Данная версия n N-API будет доступна в мажорной версии Node.js, в которой она была опубликована, и во всех последующих версиях Node.js, включая последующие мажорные версии.
Автор нативного аддона может воспользоваться гарантией прямой совместимости N-API, убедившись, что аддон использует только API, определенные в node_api.h
, а также структуры данных и константы, определенные в node_api_types.h
. Тем самым автор облегчает внедрение своего аддона, указывая производственным пользователям, что нагрузка на обслуживание их приложения увеличится не больше от добавления нативного аддона в их проект, чем от добавления пакета, написанного исключительно на JavaScript.
N-API версионируется, потому что время от времени добавляются новые API. В отличие от семантического версионирования, версионирование N-API является кумулятивным. То есть каждая версия N-API имеет то же значение, что и минорная версия в системе semver, а это означает, что все изменения, внесенные в N-API, будут обратно совместимы. Кроме того, новые N-API добавляются под экспериментальным флагом, чтобы дать сообществу возможность проверить их в производственной среде. Экспериментальный статус означает, что, хотя и были приняты меры для обеспечения того, чтобы новый API не пришлось изменять несовместимым с ABI образом в будущем, он еще не был достаточно проверен в производственной среде, чтобы быть правильным и полезным в том виде, в котором он был разработан, и, как таковой, может претерпеть несовместимые с ABI изменения, прежде чем он будет окончательно включен в предстоящую версию N-API. То есть экспериментальный N-API еще не охвачен гарантией прямой совместимости.