Загрузка Wasm в JavaScript

Последнее обновление: 30.07.2019

В прошлой теме была рассмотрена компиляции кода 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.

Компиляция кода C в Wasm

После этого в папке с исходным кодом появятся файлы 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
emrun в emscripten

И после запуска будет выполнен код javascript, который обратится к функции add из модуля wasm и выведет на веб-страницу полученную сумму.

Загрузка модуля WebAssembly в JavaScript
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850