Вызов кода JavaScript из WebAssembly

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

В прошлой теме было рассмотрено, как вызывать функции, определенные в коде wasm, через javascript. Для этого мы импортируем функции из wasm в js.

Однако может быть и противоположная ситуация: вызов кода javascript в wasm. Для этого мы, наоборот, должны импортировать вызываемые фукции js в wasm.

Для импортирования функционала из javascript в wasm применяется специльный объект импорта (importObject). По сути это обычный объект JavaScript, который представляет все функции и данные, передаваемые из JavaScript в WebAssembly. Объект импорта передается в модуль WebAssembly через второй параметр функции WebAssembly.instantiateStreaming(), которая компилирует и инстанциирует объект модуля.

Хотя спецификация не устанавливает каких-то орграничений на создания объекта импорта, то есть это обычный объект javascript, структуру которого разработчик определяет сам, исходя из того функционала, который надо импортировать. Однако при использовании Emscripten объект импорта должен обязателно иметь внутренний объект под названием env. И все функции и данные, которые передаются из JavaScript в WebAssembly, должны содержаться в этом внутреннем объекте env.

Для примера определим следующую веб-страницу index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WebAssembly Application</title>
	</head>
    <body>
	<div id="container">
	</div>
	<script>
	function updateContainerValue(value){
		document.getElementById("container").innerHTML = "Result: " + value; 
	}
	if ("WebAssembly" in window) {
	
		let importObject = { 
			env: {
				_update: updateContainerValue 
			}
		}
		WebAssembly.instantiateStreaming(fetch("hello.wasm"), importObject)
			.then(result => result.instance.exports._sum(34, 3))
			.catch(console.error);
	}
	</script>
  </body>
</html>

В коде javascript определена функция updateContainerValue, которая принимает некоторое значение и обновляет им содержимое элемента на веб-странице.

Далее формируется объект importObject - объект импорта, который будет передаваться в wasm. В этом объекте определяется внутренний объект env, который имеет свойство _update. По сути это свойство представляет выше определенную функцию updateContainerValue. Обратите внимание, что название название функции предваряется знаком подчеркивания.

Далее вызывается функция sum, которой передаются два числа и которая будет определена в коде WebAssembly.

Теперь определим код в файле hello.c:

extern void update(int n);

void sum(int a, int b)
{
	update(a + b);
}

Вначале файла подключается внешняя функция update, которой передается некоторое значение. То есть эта функция соответствует функции updateContainerValue из кода javascript. Поскольку эта функция передается через свойство под названием "_update"" объекта env, то в коде на Си мы ее получаем как функцию с именем update.

Обратите внимание, что в коде на Си название всех функций используется без знака подчеркивания: update, sum. Тогда как в javascript название этих функций предваряется знаком подчеркивания: _update, _sum. Эта условность, которую надо соблюдать.

Далее в функции sum происходит вызов внешней функции update, которой передается сумма параметров.

Скомпилируем файл wasm с помощью следующей команды:

emcc hello.c -O2 -s WASM=1 -s ONLY_MY_CODE=1 -s EXPORTED_FUNCTIONS="['_sum']" -o hello.js -s ERROR_ON_UNDEFINED_SYMBOLS=0

Здесь указывается, что мы экспортируем функцию sum. Флаг ERROR_ON_UNDEFINED_SYMBOLS=0 убирает ложные в данном случае ошибки при компиляции, связанные с импортом функции в коде Си.

Далее запустим веб-страницу index.html с помощью следующей команды:

emrun index.html
Импорт функций javascript в модуль WebAssembly

В итоге код javascript запустит код wasm, в частности, функцию sum, которая в свою очередь вызовет функцию update.

Вызов функций javascript из кода WebAssembly

Таким образом, можно вызывать код JavaScript из WebAssembly, в том числе, обновлять и изменять элементы на веб-странице.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850