В прошлой теме была рассмотрена компиляции кода WebAssembly с помощью Emscripten. Теперь чуть более детально рассмотрим, как мы можем вызывать скомпилированный код wasm из javascript.
Определим в файле hello.c простейшую программу на C:
int add(int a, int b) { return a + b; }
Файл содержит простейшую функцию, которая возвращает сумму параметров. Это весь код файла hello.c.
Скомпилируем этот файл с помощью следующей команды:
emcc hello.c -O2 -s WASM=1 -s ONLY_MY_CODE=1 -s EXPORTED_FUNCTIONS="['_add']" -o hello.js
На вход компилятору передается ряд параметров:
hello.c
: файл с исходным кодом
-O2
: определяет уровень оптимизации компилятора как для кода wasm, так и для кода js.
-s WASM=1
: указывает компилятору, что надо компилировать код в WebAssembly
-s ONLY_MY_CODE=1
: указывает, что скомпилированная единица будет содержать только тот код, который определил разработчик.
-s EXPORTED_FUNCTIONS="['_add']"
: указывает, какие функции мы сможем экспортировать и использовать в коде javascript.
При этом хотя функция в коде C называется "add", необходимо добавлять в начало названий функций символ подчеркивания.
-o hello.js
: путь к выходному файлу - hello.js.
После этого в папке с исходным кодом появятся файлы hello.wasm и hello.js. Файл hello.js нам не понадобится, поэтому его можно удалить, поскольку минимальный код загрузки кода Wasm мы определим сами.
Далее в той же папке определим новый файл index.html со следующим кодом:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>WebAssembly Application</title> </head> <body> <div id="container"> </div> <script> if ("WebAssembly" in window) { WebAssembly.instantiateStreaming(fetch("hello.wasm")) .then(result => document.getElementById("container").innerHTML = "Result: " + result.instance.exports._add(2, 5) ).catch(console.error); } </script> </body> </html>
В данном случае из wasm вызывается функция add, и ее результат выводится в блок container.
Загрузка функционала скомпилированного кода wasm и вызов его функций производится с помощью следующего кода:
WebAssembly.instantiateStreaming(fetch("hello.wasm")) .then(result => document.getElementById("container").innerHTML = "Result: " + result.instance.exports._add(2, 5) ).catch(console.error);
Компиляции модуля wasm применяется функция WebAssembly.instantiateStreaming(). Она принимает два параметра:
Первый параметр - объект Response или Promise<Response>, который представляет модуль wasm.
Второй необязательный параметр - объект, который содержит значения, которые должны быть импортированы в объект модуля.
Для загрузки файла wasm применяется функция fetch(). В качестве параметра она принимает путь к файлу wasm. Файл wasm необязательно будет загружен сразу же при вызове функции, поэтому ее результатом является объект Promise, который содержит ответ сервера в виде объекта Response.
Функция WebAssembly.instantiateStreaming получает эти данные и возвращает объект Promise<ResultObject>. ResultObject - это по сути тот объект, который инкапсулирует все функции и данные из загруженного модуля wasm. И чтобы его получить из Promise далее применяется функция then().
В функцию then в качестве первого параметра передается функция, которая выполняется в случае успешного получения объекта ResultObject из
Promise. В данном случае вместо обычной функции применяется стрелочная функция result => ...
. В данном случае
параметр result - это и есть полученный ResultObject.
Имея ResultObject, мы можем обращатья к функционалу из модуля wasm. ResultObject имеет два свойства:
instance: представляет объект WebAssembly.Instance и обеспечивает доступ ко всем экспортированным функциям
module: представляет скомпилированный модуль wasm в виде объекта WebAssembly.Module
И в конце мы можем обратиться к функции add из модуля wasm: result.instance.exports._add(2, 5)
. Стоит обратить внимание,
что для вызова функции в начале их назания ставится символ подчеркивания. Поскольку скомпилированная функция add принимает два параметра и возвращает их сумму,
то здесь функция получает два числа, а ее результат передается для вывода в элемент на веб-странице.
Также отмечу, что хотя здесь для краткости код javascript помещен на непосредственно на веб-страницу, но естественно его можно вынести в отдельный файл js и затем его подключать.
Таким образом, в одной папке будет несколько файлов.
hello.wasm
hello.js (необязателен, можно удалить)
index.html
Запустим веб-страницу index.html с помощью команды
emrun index.html
И после запуска будет выполнен код javascript, который обратится к функции add из модуля wasm и выведет на веб-страницу полученную сумму.