Как открыть файл wasm
Каким Интернетом вы пользуетесь? Мне, например, нравится приватный и безопасный.
К сожалению, неважно, что думает кто-то из нас. WebAssembly, захватывающая новая технология, которая позволяет браузерам запускать родной, низкоуровневый код, может положить конец конфиденциальности и безопасности в Интернете в том виде, в котором мы его знаем.
Короче говоря, WebAssembly - это время выполнения, которое позволяет браузерам запускать родной код. Когда я говорю "родной", я имею в виду код, написанный на низкоуровневых языках, таких как C, C++, Rust и др.
Это упрощенная схема принципов работы WebAssembly. Компилятор вводит код на LLVM-совместимом языке и создает бинарный файл .ism.
Этот файл загружается и подключается к существующему JS-коду с помощью JavaScript-интерфейса, после чего файл .wasm выполняется во время выполнения WebAssembly.
WebAssembly сейчас полностью освоена и поддерживается во всех основных браузерах.
WebAssembly предлагает исключительную производительность. Это означает, что ваши веб-сайты могут работать почти так же быстро, как обычное программное обеспечение на вашем компьютере вне браузера.
Я не могу не подчеркнуть, насколько это революционно. Она позволяет браузерам запускать программное обеспечение и игры, которые раньше были недоступны из-за проблем с производительностью. Это сделает возможным сложный опыт работы с WebVR. WebAssembly может сделать Chromebooks действительно полезным.
Есть много способов. Они стали возможны благодаря нечитаемой природе .ism-файлов. Их еще можно декомпилировать и анализировать, но это не так просто, как претендовать на JavaScript.
Небольшой скрипт отслеживания может быть включен в одну из библиотек, используемых веб-сайтом, и никто об этом не узнает. Таким образом, защита от отслеживания, как для первой стороны (Firefox), так и для третьей стороны (плагины), перестанет работать.
Во-вторых, увеличение производительности означает, что сайты теперь могут лучше использовать ваше оборудование. Крипто-валютный майнинг, например. И опять же, это будет пресловуто трудно заблокировать.
Наконец, есть проблемы с безопасностью. При такой большой мощности и низкоуровневом доступе становятся возможны определенные атаки (brute-force) и уязвимости. Будем надеяться, что сообщество с открытым исходным кодом не отстает.
Таким образом, интернет определенно не будет таким же после широкого внедрения WebAssembly.
Тем не менее, еще предстоит увидеть, насколько катастрофическими будут последствия. Спасибо за чтение и дайте мне знать в комментариях, что вы думаете о WebAssembly!
Еще больше интересной информации по программированию вы можете найти в нашем Телеграмм-канале по ссылке:
Если вы уже компилировали модуль из другого языка, используя такие инструменты как Emscripten, или загружали и запускали код, то следующим шагом будет углублённое изучение возможностей WebAssembly JavaScript API. Эта статья даст необходимые знания по этому вопросу.
Примечание: Если вы незнакомы с фундаментальными понятиями, упомянутыми в этой статье, и вам нужны дополнительные объяснения, то вам нужно сначала прочитать про Основы WebAssembly.
Несколько простых примеров
Давайте запустим несколько примеров, которые объяснят как использовать WebAssembly JavaScript API, и как использовать его для загрузки wasm-модуля в web-странице.
Примечание: вы можете найти примеры кода в нашем репозитории webassembly-examples на GitHub.
Подготовка примера
- Для начала нам нужен wasm-модуль! Возьмите наш файл simple.wasm и сохраните копию в новой директории на своём локальном компьютере.
- Далее, давайте создадим в той же директории что и wasm-модуль простой HTML-файл и назовём его index.html (можно использовать HTML шаблон если вы этого ещё не сделали).
- Теперь, для того чтобы понять что происходит в коде модуля, давайте взглянем на его текстовое представление (которое мы также встречали в Перевод из текстового формата WebAssembly в wasm):
- Во второй строчке вы видите что import имеет двухуровневое пространство имён - внутренняя функция $i импортирована из imports.imported_func . Нам нужно создать это двухуровневое пространство имён в JavaScript-объекте, который будет импортирован в wasm-модуль. Создайте <script></script> элемент в своём HTML-файле, и добавьте следующий код:
Загрузка wasm-модуля в потоке
Следующий пример (см. наш демонстрационный файл instantiate-streaming.html на GitHub и его работу вживую) показывает как использовать instantiateStreaming() чтобы загрузить wasm-модуль, импортировать JavaScript функцию в него, компилировать, создать его экземпляр и получить доступ к его экспортируемой функции. Все это в одном шаге.
Добавьте этот скрипт ниже первого блока кода:
В конце этого действия мы вызываем нашу экспортированную из WebAssembly-функцию exported_func , которая в свою очередь вызывает нашу импортированную JavaScript-функцию imported_func , которая выводит в консоль значение (42), что хранится внутри экземпляра модуля WebAssembly. Если вы сейчас сохраните пример кода и загрузите его в браузер, который поддерживает WebAssembly, вы увидите это в действии!
Примечание: Этот замысловатый и запутанный пример почти ничего не делает, но он служит иллюстрацией того, что можно одновременно использовать WebAssembly-код и JavaScript-код в ваших приложениях. Как мы утверждали ранее, технология WebAssembly не призвана заменить JavaScript. Вместо этого две технологии могут работать вместе, усиливая преимущества каждой стороны.
Загрузка wasm-модуля без потока
Эти методы не получают непосредственно доступ к байт-коду, так что требуется дополнительный шаг помещения ответа загрузки файла в объект ArrayBuffer перед компилированием и созданием экземпляра wasm-модуля.
Эквивалентный код будет выглядеть так:
Просмотр wasm в инструментах разработчика
В Firefox 54+, в отладочной панели инструментов разработчика имеется возможность отображения текстового представления любого wasm-кода, включённого в веб-страницу. Для того чтобы просмотреть его, вы можете перейти на отладочную панель и нажать на пункт "wasm://".
В ближайших версиях в Firefox, в дополнении к просмотру wasm-кода как текста, разработчики будут иметь возможность отлаживать wasm используя текстовый формат (устанавливать точки останова, изучать стек вызовов, построчно переходить, и.т.д). См. WebAssembly debugging with Firefox DevTools в видео-анонсе.
Память
В низкоуровневой модели памяти WebAssembly, память представлена как диапазон смежных байт, называемых линейной памятью (Linear Memory), которая внутри модуля читается и записывается инструкциями загрузки и размещения значений. В этой модели памяти, любая инструкция загрузки или размещения может получить доступ к любому байту всей линейной памяти. Это необходимо для полноценного представления таких концепций языков C/C++ как указатели.
В отличии от C/C++ программы, где доступный диапазон памяти ограничен процессом, память доступная отдельному экземпляру WebAssembly ограничена до одного специфического (потенциально очень маленького) диапазона, который содержится в объекте памяти WebAssembly. Это позволяет единственному web-приложению использовать множество независимых библиотек (использующих WebAssembly) которые могут иметь отдельные и полностью изолированные друг от друга диапазоны памяти.
В JavaScript-коде, объект памяти WebAssembly можно считать объектом ArrayBuffer c изменяемыми размерами. Одно веб-приложение может создавать много таких независимых объектов памяти. Вы можете создать новый объект, используя конструктор WebAssembly.Memory(), который принимает аргументы начального и максимального размера буфера (опционально).
Давайте исследуем эту возможность рассмотрев небольшой пример.
Создайте ещё одну простую HTML страницу (скопируйте HTML шаблон) и назовите её memory.html . Добавьте <script></script> элемент на страницу.
Добавьте следующую строку в начало нашего скрипта, для создания экземпляра объекта памяти:
Единицы измерения начальной ( initial ) и максимальной ( maximum ) памяти имеют фиксированный размер в 64KB. Это означает, что в нашем случае объект памяти при создании имеет 640KB, а его максимальный возможный размер будет 6.4MB.
Объект памяти WebAssembly предоставляет свой хранимый диапазон байт через getter/setter свойства buffer, которое возвращает объект ArrayBuffer. Для примера, чтобы записать число 42 в первое слово линейной памяти, вы можете сделать это:
вы можете возвратить значение используя этот код:
Попробуйте сделать это в вашем примере - сохраните то, что вы сделали, загрузите его в браузере, после чего попробуйте ввести вышеупомянутые строчки в JavaScript-консоль.
Расширение памяти
Объект памяти может быть расширен с помощью вызова метода Memory.prototype.grow() , где аргументом будет количество единиц (размером в 64KB) памяти WebAssembly:
При превышении максимального значения, указанного при создании объекта памяти, будет выброшено исключение WebAssembly.RangeError . Движок использует предоставленные верхние границы для резервирования памяти заранее, что делает расширение памяти более эффективным.
Примечание: Так как размер объекта ArrayBuffer неизменен, после успешного вызова метода Memory.prototype.grow() свойство buffer объекта памяти будет возвращать уже новый объект ArrayBuffer (с новым размером в свойстве byteLength) и любые предыдущие объекты ArrayBuffer станут в некотором роде “отсоединёнными”, или отключёнными от низкоуровневой памяти, к которой они ранее относились.
Подобно функциям, диапазоны линейной памяти могут быть импортированы или определены внутри модуля. Также, модуль имеет возможность экспортировать свою память. Это означает, что JavaScript-код может получить доступ к объекту памяти WebAssembly либо c помощью создания нового объекта через конструктор WebAssembly.Memory и передачи его в импортируемый объект, либо с помощью получения объекта памяти через экспортируемый объект (через Instance.prototype.exports ).
Более сложный пример
Давайте сделаем вышеупомянутые утверждения понятнее, рассмотрев более сложный пример работы с памятью, где WebAssembly-модуль импортирует объект памяти, который мы определили ранее, после чего JavaScript-код наполняет его с помощью массива целых чисел, а экспортируемая wasm-функция суммирует их.
Скопируйте файл memory.wasm в локальную директорию в которой вы работаете.
Примечание: вы можете увидеть текстовое представление модуля в файле memory.wat.
Откройте ваш файл memory.html и добавьте следующий код снизу вашего основного скрипта для загрузки, компилирования и создания экземпляра wasm-модуля:
Так как модуль экспортирует свою память, которая была передана экземпляру этого модуля при его создании, мы можем наполнить ранее импортированный массив прямо в линейной памяти экземпляра модуля ( mem ), и вызвать экспортированную функцию accumulate() для расчёта суммы значений. Добавьте следующий код, в обозначенном месте:
Обратите внимание на то, что мы создаём представление данных Uint32Array (en-US) с помощью свойства buffer объекта памяти ( Memory.prototype.buffer ), а не самого объекта памяти.
Импорт памяти почти такой же как импорт функций, только вместо JavaScript функций передаются объекты памяти. Импорт памяти полезен по двум причинам:
- Он позволяет JavaScript-коду получать и создать начальное содержание памяти перед или одновременно с компиляцией модуля.
- Он позволяет импортировать один объект памяти во множество экземпляров модулей, что является ключевым элементом для реализации динамического связывания в WebAssembly.
Примечание: вы можете найти полную демонстрацию в файле memory.html (см. её также вживую) — эта версия использует функцию fetchAndInstantiate() .
Таблицы
Таблица WebAssembly - это расширяемый типизированный массив ссылок, доступ к которому может быть получен из JavaScript и WebAssembly кода. Так как линейная память предоставляет расширяемый массив незащищённых байт, слишком небезопасно размещать там ссылки, так как для движка ссылка является доверенным значением, чьи байты не должны быть прочитаны или записаны кодом напрямую по причинам безопасности, переносимости и стабильности.
У таблиц есть тип элемента, который ограничивает тип возможной ссылки, который может быть размещён в таблице. В текущей версии WebAssembly, только один тип ссылки используется в WebAssembly коде - функции - и поэтому существует только один возможный тип элемента. В следующих версиях их количество будет увеличено.
Ссылки на функции необходимы для компиляции в таких языках как C/C++, которые имеют указатели на функции. В родной реализации C/C++, указатель на функцию представлен прямым адресом на код функции в виртуальном адресном пространстве процесса, и потому для ранее упомянутой безопасности, они не могут быть размещены прямо в линейной памяти. Вместо этого ссылки на функции размещаются в таблице, а её индексы, которые являются целыми числами могут быть размещены в линейной памяти и переданы куда угодно.
Когда приходит время для вызова указателя на функцию, вызывающая сторона WebAssembly предоставляет индекс, который затем может быть проверен на безопасность по таблице перед индексацией и вызовом ссылки на индексированную функцию. Таким образом, таблицы в настоящее время являются лучшим низкоуровневым примитивом, используемым для безопасной и удобной компиляции низкоуровневых возможностей языка программирования.
Таблицы могут изменятся с помощью метода Table.prototype.set() , который обновляет одно из значений в таблице, и метода Table.prototype.grow() , который увеличивает количество значений, которое может быть размещено в таблице. Это позволяет этому "непрямо-вызываемому набору функций" изменяться со временем, что необходимо для техник динамического связывания. Изменения немедленно становятся доступными с помощью метода Table.prototype.get() в JavaScript-коде и wasm-модулях.
Пример таблицы
Давайте взглянем на простой пример таблицы - модуль WebAssembly, который создаёт и экспортирует таблицу с двумя элементами: элемент под индексом 0 возвращает 13, а элемент под индексом 1 возвращает 42.
Сделайте локальную копию файла table.wasm в новой директории.
Примечание: вы можете посмотреть текстовое представление модуля в файле table.wat.
Создайте новую копию нашего HTML шаблона в той же директории и назовите его table.html.
Как и раньше загрузите, компилируйте, и создайте экземпляр вашего wasm-модуля, добавив следующий код в <script> элемент в тело документа:
Теперь давайте получим доступ к данным в таблицах - добавим следующие строки в ваш код, в обозначенном месте:
Этот код получает доступ к каждой ссылке на функцию, которая размещена в таблице, после чего вызывает её и выводит хранимое значение в консоль. Обратите внимание, что каждая ссылка на функцию получена с помощью вызова метода Table.prototype.get() , после чего мы добавили пару круглых скобок для вызова самой функции.
Примечание: вы можете найти нашу полную демонстрацию в файле table.html (см. её также вживую) — эта версия использует функцию fetchAndInstantiate() .
Глобальные переменные
WebAssembly имеет возможность создавать экземпляры глобальных переменных, доступных как в JavaScript так и в экземплярах модулей WebAssembly ( WebAssembly.Module (en-US) ) посредством импорта или экспорта. Это очень полезная возможность, которая позволяет динамически связывать несколько модулей. Для создания глобальной переменной WebAssembly внутри вашего JavaScript-кода, используйте конструктор WebAssembly.Global() (en-US) , который выглядит так:
Вы можете видеть, что он принимает 2 параметра:
- Объект, который содержит 2 свойства, описывающих глобальную переменную:
- value : это тип данных, который может быть одним из типов данных, позволенных внутри WebAssembly модуля — i32 , i64 , f32 , или f64 .
- mutable : булево значение, определяющее что переменная может изменяться.
Что мы будем с этим делать? В следующем примере мы определим глобальную, изменяемую переменную с типом i32 и значением 0.
Значение глобальной переменной будет изменено на число 42 используя свойство Global.value , а после на 43 используя экспортированную функцию incGlobal() из модуля global.wasm (это добавит 1 к установленному значению и возвратит новое).
Примечание: вы можете увидеть этот пример вживую на GitHub; смотрите также исходники.
Множественность
К этому моменту мы продемонстрировали использование всех ключевых составных элементов WebAssembly, и сейчас самое время рассказать о концепции множественности. Она позволяет WebAssembly иметь множество преимуществ с точки зрения архитектурной эффективности:
- Один модуль может иметь N экземпляров, точно так же как одно определение функции может произвести N замыканий.
- Один экземпляр модуля может использовать от 0 до 1 объекта памяти, который предоставляет “адресное пространство” экземпляра модуля. Будущие версии WebAssembly позволят иметь 0–N экземпляров объектов на один экземпляр модуля (см. Несколько таблиц и объектов памяти).
- Один экземпляр модуля может использовать от 0 до 1 объекта таблицы - это “адресное пространство для функций” экземпляра модуля используется для реализации С/С++ указателей на функции. Будущие версии WebAssembly позволят иметь 0–N экземпляров таблиц на один экземпляр модуля.
- Один объект памяти или объект таблицы может быть использован в 0-N экземплярах модулей - эти все экземпляры будут разделять одно и то же адресное пространство, позволяя выполнять динамическое связывание.
Чтобы ознакомится с множественностью в действии, смотрите нашу объясняющую статью Изменяющиеся таблицы и динамическое связывание.
Резюме
В этой статье-путеводителе по основам WebAssembly JavaScript API вы включали модули WebAssembly в среду JavaScript, использовали их функции, объекты памяти, таблицы и глобальные переменные. Мы также затронули концепцию множественности .
В этой стате мы увидим, как сделать ваши первые шаги в Wasm, попробуем передать данные из Golang в JavaScript и некоторые рецепты, которые помогут вам продвинуться дальше.
Начнем
Что ж, в названии сказано: «Wasm in Golang - это фантастика», но что такое «Wasm» в двух словах?
На домашней странице WebAssembly говорится: «WebAssembly (сокращенно Wasm) - это двоичный формат инструкций для виртуальной машины на основе стека. Wasm разработан как переносимая цель компиляции для языков программирования, позволяющая развертывать в Интернете клиентские и серверные приложения».
Теперь вы можете запускать Wasm с помощью JavaScript и NodeJS, и недавно мы увидели рождение сред выполнения Wasm, таких как проект Wasmer, позволяющих запускать Wasm повсюду.
Я люблю говорить это "wasm-файл похож на образ контейнера, но меньше по размеру и без операционной системы".
Wasm полиглот, но.
Вы можете скомпилировать файл Wasm с несколькими языками: C / C++, Rust, GoLang, Swift, . И мы даже видели появление языков, посвященных сборке Wasm, таких как AssemblyScript или многообещающий Grain.
Этим летом я решил начать с Wasm. Кажется, что для этого есть тенденция использовать Rust, но я быстро понял, что мои детские шаги будут сложными. Сложность не обязательно связана с самим языком. Самой утомительной и сложной частью были все инструменты, необходимые для запуска простого "Hello World" в браузере (1) . После некоторых поисков я обнаружил, что Golang обеспечивает довольно простую поддержку Wasm (намного проще, чем с Rust). Итак, моя домашняя работа на каникулах была сделана с Go.
Поддержка Wasm с Golang просто фантастическая. Обычно WebAssembly имеет четыре типа данных (32- и 64-разрядное целое число, 32- и 64-разрядное число с плавающей запятой), и использование функций со строковыми параметрами (или даже объектов JSON) может вызвать затруднения. К счастью, Go предоставляет файл wasm_exec.js , который упрощает взаимодействие с JavaScript API.
(1) : Через месяц я покопался более серьезно, и думаю, что это в основном документация и примеры, которые не подходят для новичков. «Первые шаги с Wasm in Rust» могут стать темой будущей статьи.
Предпосылки
Чтобы запустить примеры из этой статьи, вам понадобятся:
Кстати, для обслуживания своих страниц я использую Fastify с этим кодом:
и я использую этот файл package.json для установки Fastify:
Создать быстрый и грязный проект
Сначала создайте каталог hello-world , а затем внутри этого каталога создайте 2 файла:
со следующим исходным кодом:
Замечание: самые важные части:
- Эта строка <script src="https://dev-gang.ru/article/osnovy-wasm-in-golangeto-fantastika-hqh8jvdddr/wasm_exec.js"></script>
- И эта WebAssembly.instantiateStreaming , API JavaScript, который позволяет загрузить файл wasm.
Вам также необходимо создать файл go.mod , используя эту команду:
У вас должен получиться такое:
Создайте свой первый модуль Wasm
Перед сборкой модуля Wasm вам необходимо получить файл wasm_exec.js , после чего вы сможете запустить компиляцию:
Итак, начать довольно просто, но если вы посмотрите на размер main.wasm , вы обнаружите, что размер сгенерированного файла составляет около 2,1МБ. и честно говоря, я считаю это неприемлемым. К счастью, у нас есть удобное решение сTinyGo. Посмотрим на это.
Во-первых, что такое TinyGo? TinyGo позволяет компилировать исходный код Golang для микроконтроллеров, а также может компилировать код Go в Wasm. TinyGo - компилятор, предназначенный для использования в «маленьких местах», поэтому сгенерированные файлы значительно меньше.
Скопируйте ваш проект hello-world в новый каталог hello-world-tinygo и измените содержание файла go.mod :
Прежде чем создавать файл Wasm, на этот раз вам необходимо получить wasm_exec.js совместно с TinyGo, и тогда вы сможете запустить компиляцию:
Если вы запустите свою html-страницу, вы получите тот же результат, что и в предыдущем примере. Но посмотрите на размер main.wasm . Теперь размер составляет 223K, и это намного лучше.
Имейте в виду, что TinyGo поддерживает подмножество языка Go, поэтому еще не все доступно (tinygo.org/docs/reference/lang-support). Для моих экспериментов этого было достаточно; в противном случае продолжайте с "чистым" Go.
Моя маленькая кулинарная книга
Я видел слишком много длинных учебных пособий, которые в конце концов остановились на этом простом "hello world", не продвигаясь дальше. Они даже не объясняют, как изменять параметры функций. Часто это всего лишь украшение "getting started", никогда не продвигаясь дальше.
Поэтому сегодня я даю вам все маленькие рецепты, которые позволили мне пойти немного дальше.
Вот различные взаимодействия между Wasm и браузером, о которых я расскажу сегодня:
- Взаимодействие с DOM
- Получите строку, вызвав функцию Golang со строкой в качестве параметра
- Как вернуть объект, "читаемый" с помощью JavaScript?
- Как использовать объект JSON в качестве параметра?
- Как использовать массив в качестве параметра?
- Как вернуть массив?
Взаимодействие с DOM
Мы будем использовать "syscall/js" пакет Golang для добавления дочерних тегов в объектную модель html-документа из кода Go. Согласно документации: "Пакет js предоставляет доступ к среде хоста WebAssembly при использовании архитектуры js/wasm. Его API основан на семантике JavaScript". Этот пакет предоставляет небольшой набор функций: тип Value (представление данных Go JavaScript) и способ запроса Go от хоста JavaScript.
- Создайте новый каталог, скопировав предыдущий, и назовите его dom
- Обновите файл go.mod :
Просто измените код main.go :
- У нас есть ссылка на DOM с js.Global().Get("document")
- Мы создали элемент <h2></h2> с document.Call("createElement", "h2")
- Мы устанавливаем значение innerHTML с h2.Set("innerHTML", message)
- И, наконец, добавьте элемент в тело с помощью document.Get("body").Call("appendChild", h2)
Теперь давайте посмотрим, как сделать вызываемую функцию Go, которую мы будем использовать на нашей html-странице.
Вызов функции Go
На этот раз нам нужно "экспортировать" функцию в глобальный контекст (т.е. window в браузере, global в NodeJS). Снова GoLang пакет "syscall/js" предоставляет для этого необходимые функции.
Как обычно, создайте новый каталог first-function (используйте предыдущий пример) и обновите файл go.mod путем изменения значения модуля: module first-function .
Это исходный код main.go :
- Чтобы экспортировать функцию в глобальный контекст, мы использовали функцию: js.Global().Set("Hello", js.FuncOf(Hello)) . Функция FuncOf используется для создания типа Func .
- Функция Hello принимает два параметра и возвращает тип interface<> . Функция будет вызываться синхронно из Javascript. Первый параметр ( this ) относится к JavaScript объекту global . Второй параметр - это срез []js.Value представление аргументов, переданных вызову функции Javascript.
Нам нужно изменить файл index.html для вызова Go функции Hello :
Что изменилось? только эти 2 строки:
- console.log(Hello("Bob Morane")) : вызов Go функции Hello с "Bob Morane" в качестве параметра и отображение результата в консоли браузера.
- вызов document.querySelector("h1").innerHTML = Hello("Bob Morane") в Go функции Hello с "Bob Morane" в качестве параметра и изменение значения тега h1 .
- Создайте файл Wasm: tinygo build -o main.wasm -target wasm ./main.go
- Запустите html-страницу: node index.js
Обновите страницу, больше никаких проблем!:
Прямо сейчас у вас в руках есть почти все, что вам нужно, чтобы идти дальше. Но позвольте мне привести вам другие мои рецепты.
Другие мои рецепты
На этот раз мы передаем 2 строковых параметра в функцию Hello ( firstName и lastName ), и для возврата объекта json мы используем тип map[string]interface<> :
Вызов функции Hello из JavaScript прост:
Запустите свою страницу node index.js :
Как использовать объект Json в качестве параметра при вызове Hello?
Если я хочу использовать объект Json в качестве параметра в JavaScript, например:
Я напишу свою функцию GoLang следующим образом
- args[0] содержит объект Json
- Используйте способ извлечения значения полей Get(field_name)
Как использовать массив в качестве параметра при вызове Hello?
Как вернуть массив?
Итак, на этот раз все. Я все еще изучаю Wasm и пакет Js Golang, но я уже получил серьезное удовольствие от всего этого.
В большой войне против JavaScript появилось новое оружие, позволяющее разработчикам выбирать свой любимый стиль программирования, одновременно повышая производительность и свою продуктивность. Это оружие — WebAssembly, которое может произвести революцию в веб-разработке на стороне клиента.
На практике WebAssembly реализуется разработчиками браузеров на основе существующего JavaScript-движка. По сути, он предназначен для замены JavaScript как целевого языка. Например, вместо компиляции TypeScript в JavaScript его разработчики теперь могут компилировать свой код в WebAssembly. Иными словами, это не новая виртуальная машина, это новый формат для той же самой виртуальной машины JavaScript, которая включена в каждый браузер. Это позволит использовать существующую инфраструктуру JavaScript без использования самого JavaScript.
4–5 декабря, Онлайн, Беcплатно
Разработка MVP была завершена в марте 2017, и сейчас есть готовые реализации для всех основных браузеров.
Почему это важно?
Во-первых, новый формат WebAssembly обещает значительное увеличение производительности парсинга. Как сказано в FAQ WebAssembly, тип бинарного формата, используемый в WebAssembly, может быть декодирован гораздо быстрее, чем JavaScript может быть пропарсен (эксперименты показывают более чем 20-кратную разницу). На мобильных устройствах большому скомпилированному коду может запросто потребоваться 20–40 секунд только на парсинг, поэтому встроенное декодирование (особенно в сочетании с такими технологиями, как улучшенная по сравнению с gzip потоковая подача для сжатия) имеет решающее значение для обеспечения хорошего пользовательского опыта.
Обратите внимание, что речь идёт о производительности парсинга, а не о производительности исполнения, т.к. во многих случаях будет использоваться существующий JavaScript-движок. Однако даже это позволит использовать в вебе ПО, которое раньше было бы нецелесообразно разрабатывать, например: виртуальные машины, виртуальную реальность, распознавание изображений и многое другое.
Первыми пользователями wasm, вероятно, будут разработчики игровых движков, поскольку они всё время ищут, как бы улучшить производительность. До WebAssembly лучшим, на что они могли надеяться, был asm.js (упрощённый JavaScript, оптимизированный для скорости), который был хорошей технологией, но не очень полезной для многих игр.
Прим.перев. На самом деле, Rust уже умеет компилировать в wasm напрямую, без Emscipten. Кроме того, с момента написания статьи произошли изменения: Autodesk сообщила о прекращении разработки и продажи Stingray.
Зачем это вам
Приход WebAssembly означает, что вам больше не придётся использовать JavaScript для веба, только потому что это единственное, что выполняется в браузере. JavaScript имеет плохую репутацию, хотя на самом деле это хороший язык в том, для чего он предназначен: позволяет быстро писать небольшие скрипты. Однако в настоящее время вы вынуждены использовать его для всего, что запускается в вебе, и это проблема для многих крупных проектов.
Конечно, вы можете использовать лучшие версии JavaScript, такие как TypeScript или даже новые языки вроде Kotlin. Но, в конце концов, все они должны быть скомпилированы в JavaScript. В свою очередь это создало проблемы для разработчиков языка JavaScript, которые должны поддерживать практически все возможные сценарии и все стили программирования. WebAssembly изменит это и позволит всем сосредоточиться на том, что у них получается лучше всего.
Более того, вы можете создать свою собственную реализацию для своих нужд. Вы можете создать оптимизированный компилятор для своего языка. Его можно создать с нуля, а можно добавить поддержку WebAssembly в существующий компилятор. Таким образом, вы можете воспользоваться всеми модулями WebAssembly.
Например, вы можете создать компилятор WebAssembly для DSL, который вы используете внутри вашей компании, и запустить его в сети на стороне клиента без использования настраиваемых плагинов, таких как Oracle Java Plug-in или Adobe Flash.
Как это работает
Основополагающий принцип WebAssembly — хорошая интеграция с существующим миром JavaScript, от технических характеристик, таких как совместимость и общие политики безопасности, до интеграции инструментов, таких как поддержка функции View Source браузеров.
Для достижения этой цели WebAssembly определяет как двоичный формат, так и эквивалентный текстовый формат для инструментов и людей. Технически текстовый формат использует S-выражения, поэтому он будет выглядеть следующим образом:
Однако инструменты, вероятно, покажут нечто более похожее на это (пример из документации):
C++ BINARY TEXT Если вас интересует, почему в качестве примера используется C++, то это из-за того, что целью начального выпуска (MVP) WebAssembly была поддержка C/C++. Поддержка других языков будет позже; в данный момент они находятся в разработке. Этот выбор был сделан по нескольким техническим и практическим причинам:
- MVP WebAssembly не поддерживает сборку мусора (в разработке);
- Реализация компилятора C/C++ в WebAssembly может полагаться на проверенные временем инструменты вроде LLVM (один из наиболее используемых наборов инструментов для компилятора).
Разработчики WebAssembly используют LLVM для сокращения объёма работы, необходимой для получения рабочего продукта. Кроме того, это позволило им легко интегрироваться с другими инструментами, которые работают с LLVM, такими как Emscripten.
Наличие MVP даёт обычным разработчикам возможность тестировать и использовать WebAssebmly, что позволяет соответственно его улучшать.
Инструменты WebAssembly
- Записывать WebAssembly в текстовом формате и преобразовывать его в двоичный файл с использованием предоставленных инструментов;
- Использовать сторонний инструмент, основанный на этих инструментах.
Первый вариант не очень практичен для общего использования, но он подойдёт, если вы хотите получить представление о формате или начать работу по его интеграции в свои собственные инструменты. В настоящее время есть два набора инструментов: WebAssembly Binary Toolkit и Binaryen.
WABT включает в себя инструменты для разработки и/или использования в инструментах, предназначенных для работы с WebAssembly:
- Он отлично поддерживает спецификацию формата;
- Он может конвертировать в текстовый формат и из него;
- Он включает в себя интерпретатор.
Иными словами, он обеспечивает чистый и лёгкий доступ к данным в формате WebAssembly, чтобы вы могли с ними работать.
С другой стороны, Binaryen — это мощный набор инструментов, предназначенный для использования в инфраструктуре компилятора:
- Он может работать с кодом WebAssembly или графом потока управления, предназначенным для компиляторов;
- Он оптимизирует код многими способами, с использованием как стандартных техник оптимизации, так и специально рассчитанных на WebAssembly;
- Он может компилировать из и в asm.js, Rust MIR (промежуточный язык для Rust) и LLVM.
Таким образом, этот набор инструментов готов к интеграции в ваш бэкенд.
По сути, оба позволяют создавать инструменты, которые управляют WebAssembly, но WABT предназначен для инструментов, которые используются во время разработки (например, статический анализ), в то время как Binaryen предназначен для поддержки WebAssembly в компиляторах.
Эти инструменты отлично подходят для тех, кто разрабатывает инструменты и продукты, связанные с компилятором: они предоставляют как инструменты для разработки, так и готовые к использованию в продакшене. Однако они не идеальны для обычных разработчиков, поэтому для них есть более простой способ.
Если вам нужно создать такие инструменты, есть ещё и второй вариант — написать всё с нуля. Этот вариант подойдёт, если вам нужен собственный компилятор или интерпретатор для вашего собственного языка, нужна лучшая производительность или лёгкий инструмент. В этом деле вам может помочь библиотека WasmCompilerKit. Это Kotlin-библиотека, которую вы можете использовать для загрузки WASM-файлов, их изменения и создания. Учитывая то, что она написана на Kotlin, вы можете использовать её также с Java и Scala.
Использование WebAssembly
Если вы просто разработчик, заинтересованный в использовании WebAssembly, то для знакомства с ним вы можете использовать Emscripten (SDK). Emscripten — это набор инструментов, который уже используется для компиляции C/C++ в asm.js. С Emscripten вам будет легче использовать ранее упомянутый Binaryen и интегрировать его со своим собственным набором инструментов.
После установки Emscripten или его компиляции из исходного кода необходимо установить binaryen:
Затем для того, чтобы активировать среду для компиляции и проверить, что установлены правильные пути и переменные, нужно использовать следующие команды:
Наконец, вы можете писать ваш код:
И затем скомпилировать в WebAssembly и увидеть результат в браузере:
Первая команда создаст три файла: модуль WASM, HTML-файл, который показывает код в действии, и JS-файл, который запускает модуль и отвечает за всё, что нужно для его запуска. Параметр WASM=1 говорит Emscripten, что мы хотим сгенерировать модуль WASM вместо asm.js-файла.
Вы также можете вывести свой код в настраиваемый шаблон, используя флаг --shell-file . Emscripten SDK содержит базовый шаблон в этом месте: (папка с esmdk)/emscripten/incoming/src/shell_minimal.html . Скопируйте этот файл в свой проект и адаптируйте его под свои нужды (например, добавьте остальную часть вашего JS-кода).
Также вы можете напрямую сгенерировать JS-файл, но это не рекомендуется на данный момент, поскольку коду нужно заботиться о таких низкоуровневых вещах, как распределение памяти, утечки памяти и т.д.
Конечная цель состоит в том, чтобы подключать модуль WebAssembly так же легко, как подключается код JavaScript, с помощью HTML-тега <script type = "module"> , но это ещё только впереди.
Взаимодействие между C и JavaScript
Взаимодействие между C и JavaScript — проблема для Emscripten. Первое, что вам нужно сделать — это подключить заголовочный файл emscripten:
Самый простой способ вызвать JavaScript — использовать функцию emscripten_run_script :
Вызвать C-функцию из JavaScript немного сложнее. Во-первых, вы должны сделать её доступной из кода C/C++, потому что по умолчанию Emscripten делает недоступными все функции C, кроме основной ( main ). Необходимо добавить модификатор EMSCRIPTEN_KEEP_ALIVE ко всем функциям, которые вы хотите использовать в JavaScript:
Если вы пишете на C++, не забудьте поместить любую функцию, которую вы хотите сделать доступной, внутри блока extern "C" , чтобы C++ не задекорировал имя функции (это то, что делает C++, а не баг WebAssembly или Emscripten).
Во-вторых, вам необходимо скомпилировать модуль WebAssembly с параметром NO_EXIT_RUNTIME , чтобы избежать остановки среды выполнения при выходе из основной функции, что сделало бы невозможным вызов кода C из JavaScript:
В-третьих, вы не можете напрямую вызвать свою C-функцию, поэтому вы должны использовать следующий синтаксис:
Есть три типа на выбор: number, string и array.
Если вам нужно использовать функцию в JS-коде несколько раз, вы можете обернуть её с помощью функции cwrap :
Вы можете легко проверить, как это работает, в консоли:
Подводим итоги
Это было короткое введение в WebAssembly: что это такое, почему это важно и как вы можете это использовать. WebAssembly станет отличной платформой для дальнейшей эволюции веба: это сделает веб-разработку проще и эффективней.
Его разработка поддерживается людьми из Mozilla, Microsoft, Google и Apple. Внимание к WebAssembly — ещё одно доказательство его важности.
Вы можете использовать WebAssembly уже сегодня и посмотреть подробную документацию на сайте MDN. Если вас интересует более подробная информация о формате, вы можете прочитать её на официальном сайте. Вы также можете посмотреть документацию Emscripten, чтобы понять детали, связанные с взаимодействием между JavaScript и C/C++.
Читайте также: