Экспорт и импорт
Учимся открывать функции наружу и принимать функции снаружи.
Export делает внутреннюю сущность модуля видимой для хоста, а import — наоборот, объявляет, что модуль ждёт извне.
Зачем экспорт
Сама по себе функция внутри модуля недоступна снаружи — её имя $add существует только внутри. Чтобы JavaScript смог её вызвать, функцию нужно экспортировать под публичным строковым именем:
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(export "add" (func $add)))
Теперь из JS функция доступна как instance.exports.add. Обратите внимание: внутреннее имя $add и публичное "add" — разные вещи, их можно называть по-разному. Экспортировать можно не только функции, но и память, таблицы и глобальные переменные.
Сокращённая форма экспорта
Экспорт можно объявить прямо при определении функции — это короче:
(module
(func (export "add") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add))
Зачем импорт
Wasm-модуль изолирован: у него нет доступа к консоли, времени, случайным числам — ни к чему из внешнего мира. Чтобы что-то получить снаружи, модуль импортирует функцию, обещая её сигнатуру. Хост (JS) обязан предоставить реализацию при создании экземпляра. Импортируем функцию логирования:
(module
(import "console" "log" (func $log (param i32)))
(func (export "run")
i32.const 42
call $log)) ;; вызовет переданную снаружи функцию
Здесь "console" — это модуль импорта, "log" — имя внутри него. JS при инстанцировании передаст объект { console: { log: (x) => ... } }.
Граница изоляции
Импорты и экспорты — это единственные двери в модуль и из него. Wasm не может вызвать что-либо, что ему явно не импортировали, и не отдаёт ничего, что явно не экспортировал. Это основа модели безопасности Wasm: песочница с чётко описанным интерфейсом.
импорты (что нужно снаружи)
|
v
+-------------+
--> | МОДУЛЬ | --> экспорты (что отдаёт наружу)
+-------------+
(всё остальное — недоступно)
Как работает под капотом
При создании экземпляра (WebAssembly.instantiate) движок сверяет импорты модуля с переданным объектом import. Если модуль ждёт console.log с сигнатурой (param i32), а вы не передали такую функцию — инстанцирование упадёт с ошибкой ещё до запуска кода. Это проверка контракта: модуль не запустится, пока все его внешние зависимости не удовлетворены.
Частые ошибки
- Забыли экспорт — функция есть, но
instance.exports.addравноundefined. - Не передали импорт — модуль ждёт
env.log, а в import-объекте его нет → ошибка инстанцирования. - Несовпадение сигнатур импорта — модуль ждёт
(param i32), а вы рассчитываете на два аргумента: контракт нарушен.
Итоги
exportоткрывает функцию/память наружу под строковым именем.importобъявляет внешнюю зависимость, которую хост обязан предоставить.- Импорты и экспорты — единственные двери модуля, основа песочницы.
- Несовпадение контракта импорта ломает инстанцирование сразу.