![image](https://habrastorage.org/webt/6r/kw/r1/6rkwr16xmpxt2b1prdbnizbnjdo.jpeg)
Книга была написана, чтобы помочь вам понять, что такое WebAssembly, как он работает и что с ним можно и нельзя сделать. Она показывает разные варианты сборки модуля WebAssembly в зависимости от ваших потребностей. Мы начинаем с простых примеров и затем переходим к более сложным темам, например к динамическому связыванию, параллельной обработке и отладке.
Создание модуля WebAssembly, вызывающего JavaScript
В главе 4 вы создали модуль WebAssembly, который вызывал JavaScript-код с помощью вспомогательной функции ccall из Emscripten. Буфер передавался в качестве параметра функции модуля, чтобы в случае возникновения проблемы можно было вернуть сообщение об ошибке, поместив его в буфер. Если возникает проблема, то JavaScript считывает строку из памяти модуля и затем отображает сообщение пользователю, как показано на рис. 5.1.
Представьте, что вместо передачи буфера функции модуля в случае возникновения проблемы модуль может просто передать сообщение об ошибке непосредственно в ваш JavaScript-код, как показано на рис. 5.2.
Используя набор инструментов Emscripten, вы можете взаимодействовать с кодом JavaScript из вашего модуля тремя способами.
- Использовать макросы Emscripten. К ним относятся серия макросов emscripten_run_script, макрос EM_JS и серия макросов EM_ASM.
- Добавить собственный JavaScript-код в файл JavaScript в Emscripten, который можно использовать напрямую.
- Задействовать указатели на функции, в которых код JavaScript указывает функцию, вызываемую модулем. Мы рассмотрим этот подход в главе 6.
![image](https://habrastorage.org/webt/de/do/ny/dedonyfqbyasoitynvs2a7z6cg4.png)
При любом способе взаимодействия с JavaScript из модуля один подход может работать лучше, чем другой, в определенных обстоятельствах.
1. Макросы Emscripten могут быть весьма полезны при отладке или когда нужно только взаимодействие с кодом JavaScript напрямую. По мере увеличения сложности кода макроса или количества взаимодействий с JavaScript вы можете рассмотреть возможность отделения кода макроса от кода на C или C++.. Это следует сделать, чтобы упростить сопровождение кода и вашего модуля, и веб-страницы.
Когда используются серии макросов EM_JS и EM_ASM, на самом деле компилятор Emscripten создает необходимые функции и добавляет их в сгенерированный файл JavaScript в Emscripten. Вызывая макросы, модуль WebAssembly в действительности вызывает сгенерированные функции JavaScript.
СПРАВКА
Более подробную информацию о макросах Emscripten, в том числе о том, как их использовать, можно найти в приложении В.
2. Как вы увидите в данной главе, вызывать JavaScript напрямую несложно, и это несколько упростит JavaScript вашего сайта. Если вы планируете вызывать функции из функции JavaScript, размещенной в сгенерированном Emscripten JavaScript-коде, то вам необходимо иметь какое-то представление об основном коде JavaScript. Если вы поставляете модуль третьей стороне, то потребуются четкие инструкции по правильной настройке модуля, чтобы не было ошибок, — например, о том, что функция не существует.
ПРЕДУПРЕЖДЕНИЕ
Если вы планируете использовать этот подход вместе с Node.js, то JavaScript-код, который вы добавляете в сгенерированный файл JavaScript, должен быть автономным. Работа с Node.js рассматривается более подробно в главе 10, но, по сути, из-за того, как Node.js загружает файл JavaScript в Emscripten, код в этом файле не может вызывать ваш основной JavaScript-код.
3. В главе 6 вы увидите, что использование указателей на функции дает гораздо больше гибкости, поскольку модулю не нужно знать, какие функции существуют в вашем JavaScript-коде. Вместо этого модуль просто вызовет предоставленную JavaScript-функцию. Дополнительная гибкость указателей на функции связана с немного большей сложностью, поскольку такой подход требует большего количества кода на JavaScript, чтобы все работало правильно.
Вместо того чтобы позволять Emscripten создавать функции JavaScript с помощью макросов, вы можете определить собственный JavaScript, который будет включен в файл JavaScript в Emscripten. Мы изучим такой подход в данной главе.
В рамках этого сценария мы изменим модуль валидации, созданный в главе 4, чтобы при возникновении проблемы при валидации сообщение об ошибке не передавалось обратно вызывающей функции с помощью параметра.. Вместо этого вы сделаете следующее (рис. 5.3).
- Если была найдена проблема с пользовательским вводом, то пусть модуль вызовет функцию JavaScript, которую вы поместите в созданный Emscripten файл JavaScript.
- Функция JavaScript будет принимать указатель от модуля и считывать сообщение об ошибке из памяти модуля.
- Затем она передаст сообщение в основной JavaScript веб-страницы, где будет обработано обновление пользовательского интерфейса с полученной ошибкой.
![image](https://habrastorage.org/webt/ok/rb/ug/okrbugdyignj8n4svd-tp-_ihxm.png)
Использование C или C++ для создания модуля со связующим кодом EMSCRIPTEN
Вернемся к логике валидации в C++, созданной в главе 4, и изменим ее так, чтобы она могла взаимодействовать с кодом на JavaScript. Добавим стандартную библиотеку C и вспомогательные функции Emscripten — это рекомендуемый способ создания модуля, предназначенного для использования в производственной среде. Позже в данной главе мы рассмотрим и другой подход к созданию модуля WebAssembly, который не включает стандартную библиотеку C или вспомогательные функции Emscripten.
Как показано на рис. 5.4, шаги по созданию модуля будут аналогичны шагам в главе 4.
- Измените код на C++ так, чтобы он больше не получал строковый буфер и вместо этого вызывал функцию JavaScript в случае проблемы с валидацией.
- Определите JavaScript-код, который нужно добавить в созданный Emscripten файл JavaScript.
- Дайте Emscripten команду сгенерировать связующие файлы WebAssembly и JavaScript.
- Скопируйте созданные файлы для использования в браузере.
- Создайте веб-страницу, а затем напишите JavaScript-код, необходимый для взаимодействия с модулем WebAssembly.
![image](https://habrastorage.org/webt/fo/e9/rf/foe9rfn3m_6pzj6zgz9qlp12p64.png)
Внесение изменений в код на C++
На рис. 5.5 видно, что первый шаг процесса — изменение кода на C++, чтобы он больше не получал строковый буфер. Вместо этого код вызовет функцию JavaScript, передав ей сообщение об ошибке при обнаружении проблемы с валидацией.
![image](https://habrastorage.org/webt/ot/2y/ds/ot2ydsfofwpcxjkillwvyus5qc4.png)
В папке WebAssembly создайте папку Chapter 5\5.1.1 EmJsLibrary\source\ для файлов, которые будут использоваться в этом подразделе. Скопируйте файл validate.cpp из папки WebAssembly\Chapter 4\4.1 js_plumbing\source\ в новую папку source. Откройте файл validate.cpp в своем любимом редакторе.
Сейчас вы измените код C++ так, чтобы вызвать функцию, определенную в JavaScript. Поскольку функция не является частью кода на C++, нужно сообщить компилятору о сигнатуре функции, добавив ключевое слово extern перед сигнатурой. Это позволяет компилировать код C++, ожидая, что функция будет доступна при запуске кода. Когда компилятор Emscripten видит сигнатуру функции, он создает для нее запись импорта в модуле WebAssembly. При создании экземпляра модуля фреймворк WebAssembly увидит запрошенный импорт и будет ожидать, что в JavaScript-файле будет предоставлена соответствующая функция.
Будущая функция в JavaScript принимает указатель const char* для параметра, который будет хранить сообщение об ошибке, если возникнет проблема с проверкой. Функция не возвращает значение. Чтобы определить сигнатуру функции, добавьте следующую строку кода в блок extern «C» и перед функцией ValidateValueProvided в файле validate.cpp:
extern void UpdateHostAboutError(const char* error_message);
Поскольку вы больше не собираетесь передавать буфер в модуль, необходимо удалить параметры char* return_error_message из функций. Кроме того, любой код, вызывающий strcpy для копирования сообщения об ошибке в буфер, теперь должен будет вместо этого вызвать функцию UpdateHostAboutError.
Измените функцию ValidateValueProvided, убрав параметр return_error_message, и добавьте вызов функции UpdateHostAboutError, а не strcpy, как показано ниже:
![image](https://habrastorage.org/webt/0n/ph/c0/0nphc0-npnd95fq5exkq32b44re.png)
Подобно функции ValidateValueProvided, измените функцию ValidateName, чтобы она больше не получала параметр return_error_message, и удалите его из вызова функции ValidateValueProvided. Измените код, чтобы теперь сообщение об ошибке передавалось в функцию UpdateHostAboutError вместо использования strcpy, как показано ниже:
![image](https://habrastorage.org/webt/xs/pm/j_/xspmj_rtzdvqzk1ue8n5rw5pf3y.png)
В функции IsCategoryIdInArray никаких изменений не требуется.
Наконец, необходимо внести те же изменения, что и для функций ValidateValueProvided и ValidateName, в функцию ValidateCategory, как показано в листинге 5.1.
![image](https://habrastorage.org/webt/gy/ow/nj/gyownjxf1dhnjiivrwnj8ye4wyq.png)
![image](https://habrastorage.org/webt/9p/lz/kx/9plzkxdhuld3yitpggywnb-3bc4.png)
Создание кода на JavaScript и добавление его в сгенерированный Emscripten JavaScript-файл
После того как код на C++ будет изменен, можно сделать следующий шаг — создать код на JavaScript и добавить его в JavaScript-файл, созданный Emscripten (рис. 5.6).
![image](https://habrastorage.org/webt/j1/rp/kx/j1rpkxry0maaem1_jzay2rrqegk.png)
При написании кода на JavaScript, который будет включен в созданный Emscripten JavaScript-файл, модуль WebAssembly создается немного другим способом. В этом случае вы определите свою функцию UpdateHostAboutError в JavaScript до того, как дадите Emscripten указание скомпилировать код C++, поскольку с помощью компилятора Emscripten вы можете объединить ваш JavaScript-код с остальной частью JavaScript-кода, генерируемого Emscripten.
Чтобы ваш JavaScript был включен в созданный Emscripten JavaScript-файл, необходимо добавить его в объект LibraryManager.library в Emscripten; для этого можно использовать функцию mergeInto в Emscripten, которая принимает два параметра:
- объект, к которому нужно добавить свойства, — в данном случае объект LibraryManager.library;
- объект, свойства которого будут скопированы в первый объект, — в нашем случае ваш код на JavaScript.
Вы создадите JavaScript-объект, который будет содержать свойство UpdateHostAboutError; значение его — функция, получающая указатель на сообщение об ошибке. Функция считывает строку из памяти модуля с помощью вспомогательной функции в Emscripten — UTF8ToString — и затем вызывает JavaScript-функцию setErrorMessage, которая является частью основного кода JavaScript вашей веб-страницы.
В папке WebAssembly\Chapter 5\5.1.1 EmJsLibrary\source\ создайте файл mergeinto.js, откройте его в своем любимом редакторе и добавьте следующий фрагмент кода:
![image](https://habrastorage.org/webt/sp/bx/cz/spbxczkkcnf5vghpbapv7aqtj4s.png)
Компиляция кода в модуль WebAssembly
Изменив код на C++ и создав функцию JavaScript, которую нужно включить в JavaScript-файл, созданный Emscripten, можно сделать следующий шаг. Как показано на рис. 5.7, на данном этапе Emscripten скомпилирует код в модуль WebAssembly. Кроме того, Emscripten получит указание добавить код из вашего файла mergeinto.js в сгенерированный файл JavaScript.
![image](https://habrastorage.org/webt/j7/3r/tm/j73rtmaplwxd36jrlvrhigbci2s.png)
Чтобы дать компилятору Emscripten указание включить ваш код на JavaScript в сгенерированный JavaScript-файл, нужно использовать флаг --js-library, за которым следует путь к добавляемому файлу. Чтобы быть уверенными в том, что вспомогательные функции Emscripten, необходимые вашему коду на JavaScript, включены в сгенерированный JavaScript-файл, вы должны указать их при компиляции кода на C++, добавив их в массив параметра EXTRA_EXPORTED_RUNTIME_METHODS. Укажите следующие вспомогательные функции Emscripten:
- ccall — используется JavaScript-кодом веб-страницы для вызова модуля;
- UTF8ToString — применяется JavaScript-кодом, который вы написали в файле mergeinto.js, для чтения строк из памяти модуля.
Чтобы скомпилировать код в модуль WebAssembly, откройте командную строку, перейдите в папку, в которой вы сохранили файлы validate.cpp и mergeinto.js, и выполните следующую команду:
emcc validate.cpp --js-library mergeinto.js
➥ -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','UTF8ToString']
➥ -o validate.js
Если вы откроете сгенерированный Emscripten файл JavaScript, validate.js, и выполните поиск функции UpdateHostAboutError, то увидите, что функция, которую вы сейчас определили, является частью сгенерированного JavaScript-файла:
function _UpdateHostAboutError(errorMessagePointer) {
setErrorMessage(Module.UTF8ToString(errorMessagePointer));
}
Одной из приятных особенностей добавления функций в сгенерированный файл JavaScript является то, что если, помимо UpdateHostAboutError, в файле есть несколько других функций, то будут включены только те, которые фактически вызываются кодом модуля.
Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — WebAssembly
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.