Главная | Все статьи | Код

Введение в интерфейсы WebAssembly

Время чтения статьи ~7 минут
Введение в интерфейсы WebAssembly главное изображение

WebAssembly — портируемый бинарный формат. Это значит, что один и тот же файл может быть запущен везде. Но каждая платформа, на которой мы захотим запускать этот самый «один и тот же файл», должна уметь выполнять Wasm-код — и делать это максимально быстро и максимально безопасно.

Wasmer — это среда исполнения WebAssembly, написанная на Rust. И среда эта — встраиваемая. Она может быть включена в состав программ на Rust, а ещё её можно подключить из других языков: PHP (php-ext-wasm), Python (PyPI:wasmer), Ruby (Gem:wasmer) и других.

Wasmer полностью сфокусирован на запуске модулей WebAssembly на стороне сервера. Мы начинали с запуска сгенерированных с помощью Emscripten модулей, но постепенно добавляли поддержку других ABI (application binary interface, двоичный интерфейс приложений) — WASI, Wascap и так далее.

Вы можете запускать разные программы с каждым ABI, например, Nginx (Emscripten) и Cowsay (WASI).

Со временем мы поняли, что среда исполнения выполняла много проверок перед запуском экземпляра приложения, чтобы убедиться, что модуль совместим с определенным ABI (Emscripen или WASI). Это означает проверку соответствия импортов и экспортов модуля ожиданиям среды. Например, речь идёт о проверке сигнатур функций или совпадении глобальных типов.

Оказывается, эти проверки важны не только потому, что позволяют убедиться в совместимости модуля со средой исполнения. Они также полезны тем, что позволяют убедиться в совместимости модуля с конкретным API.

Это может быть ключом к созданию экосистемы плагинов для любой программы, использующей WebAssembly как часть системы плагинов, в которой каждый модуль должен быть совместимым с определённым интерфейсом, например, с заголовочными файлами в C.

С помощью интерфейсов WebAssembly и WAPM (WebAssembly Package Manager) вы можете легко использовать всю экосистему WAPM для создания, верификации и распространения плагинов.

Поэтому мы решили поработать над способами проверки и предложить их как стандарт определения наборов импортов и экспортов, которые должны быть в модуле.

Покажите мне интерфейсы Wasm

Интерфейсы WebAssembly созданы под влияением ЛИСП-подобного текстового формата Wasm. Мы намеренно настаивали, чтобы формат не зависел от будущих предложений, так как хотели, чтобы интерфейсы были близки к внутренним форматам Wasm (только четыре типа: i32, i64, f32, f64).

Ниже представлены интерфейсы WebAssembly для WASI:


(interface "wasi_unstable"

  ;; Функции импорта

  (func (import "wasi_unstable" "args_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "args_sizes_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "clock_res_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "clock_time_get") (param i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "environ_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "environ_sizes_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_advise") (param i32 i64 i64 i32) (result i32))

  (func (import "wasi_unstable" "fd_allocate") (param i32 i64 i64) (result i32))

  (func (import "wasi_unstable" "fd_close") (param i32) (result i32))

  (func (import "wasi_unstable" "fd_datasync") (param i32) (result i32))

  (func (import "wasi_unstable" "fd_fdstat_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_fdstat_set_flags") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_fdstat_set_rights") (param i32 i64 i64) (result i32))

  (func (import "wasi_unstable" "fd_filestat_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_filestat_set_size") (param i32 i64) (result i32))

  (func (import "wasi_unstable" "fd_filestat_set_times") (param i32 i64 i64 i32) (result i32))

  (func (import "wasi_unstable" "fd_pread") (param i32 i32 i32 i64 i32) (result i32))

  (func (import "wasi_unstable" "fd_prestat_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_prestat_dir_name") (param i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_pwrite") (param i32 i32 i32 i64 i32) (result i32))

  (func (import "wasi_unstable" "fd_read") (param i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_readdir") (param i32 i32 i32 i64 i32) (result i32))

  (func (import "wasi_unstable" "fd_renumber") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_seek") (param i32 i64 i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_sync") (param i32) (result i32))

  (func (import "wasi_unstable" "fd_tell") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "fd_write") (param i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_create_directory") (param i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_filestat_get") (param i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_filestat_set_times") (param i32 i32 i32 i32 i64 i64 i32) (result i32))

  (func (import "wasi_unstable" "path_link") (param i32 i32 i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_open") (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_readlink") (param i32 i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_remove_directory") (param i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_rename") (param i32 i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_symlink") (param i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "path_unlink_file") (param i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "poll_oneoff") (param i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "proc_exit") (param i32))

  (func (import "wasi_unstable" "proc_raise") (param i32) (result i32))

  (func (import "wasi_unstable" "random_get") (param i32 i32) (result i32))

  (func (import "wasi_unstable" "sched_yield") (result i32))

  (func (import "wasi_unstable" "sock_recv") (param i32 i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "sock_send") (param i32 i32 i32 i32 i32) (result i32))

  (func (import "wasi_unstable" "sock_shutdown") (param i32 i32) (result i32))

)

Это значит, что модуль, в котором реализуется интерфейс WASI, должен быть совместимым с представленными сигнатурами.

Как использовать интерфейсы Wasm

Некоторые проекты уже используют представленный синтаксис для определения модулей WebAssembly. Пионером интеграции интерфейсов Wasm стал WAPM.

Если вы используете wapm cli, модули будут автоматически проверяться с помощью имеющегося интерфейса перед публикацией в реестре. Так мы убеждаемся в совместимости новых модулей с ABI, которые они призваны реализовывать.

В настоящее время в WAPM доступны такие интерфейсы:

  • WASI. Это модульный системный интерфейс Wasm (WASI — WebAssembly System Interface). Он сфокусирован на безопасности и портативности.
  • Wascap. Это набор стандартов и соглашений, которые заполняют пробел в облачной среде модулей Wasm.

Ручная проверка интерфейса модуля

Когда вы используете WAPM и поле interfaces в кофигурациях, мы проверяем модуль на совместимость с интерфейсом. Также мы создали бинарный файл wasm-interface, который доступен на WAPM:

wapm install -g wasm-interface
# Run the checks
wasm-interface --dir=. check wasi-module.wasm -i interface.wai

Автоматическая проверка интерфейсов для модуля с помощью WAPM

Публиковать модули для WASI или Wascap очень просто. Убедитесь, что используете последнюю версию wasmer и wapm с помощью команды wasmer self-update.

Отредактируйте файл wapm.toml и добавьте секцию [module.interfaces]:

[package]
name = "example-cli"
version = "0.0.1"
description = "An example WASI cli app"
repository = "https://github.com/wapm-packages/example-cli"
homepage = "https://github.com/wapm-packages/example-cli"

[[module]]
name = "example-cli"
source = "example-cli.wasm"

[module.interfaces]

# We indicate this module uses the WASI interface here

wasi = "0.0.0-unstable"

интерфейс

Как только вы добавите новый интерфейс в модуль и опубликуете его на WAPM, он появится в UI. Смотрите тег WASI рядом с названием.

Как создать новый интерфейс с помощью WAPM

Скоро каждый желающий сможет создавать новые интерфейсы WebAssembly на WAPM. Мы решили начать с ручного создания новых интерфейсов, чтобы ускорить итерации при необходимости небольших изменений синтаксиса.

Адаптированный перевод статьи Introducing WebAssembly Interfaces by Syrus Akbary. Мнение автора оригинальной публикации может не совпадать с мнением администрации «Хекслета».

Аватар пользователя Дмитрий Дементий
Дмитрий Дементий 14 октября 2019
0
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 25 апреля
профессия
от 6 183 ₽ в месяц
Ручное тестирование веб-приложений
4 месяца
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 25 апреля
профессия
от 5 025 ₽ в месяц
новый
Сбор, анализ и интерпретация данных
9 месяцев
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 25 апреля
профессия
от 5 840 ₽ в месяц
Создание веб-приложений со скоростью света
5 месяцев
c опытом
Старт 25 апреля
профессия
от 9 900 ₽ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 25 апреля
профессия
от 6 300 ₽ в месяц
Разработка бэкенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 25 апреля
профессия
новый
Автоматизированное тестирование веб-приложений на JavaScript
8 месяцев
c опытом
в разработке
Старт 25 апреля
профессия
Верстка с использованием последних стандартов CSS
5 месяцев
с нуля
Старт в любое время