Первоначально опубликовано на сайте wintermade.it 22 декабря 2018 г.

Некоторое время назад я написал компилятор для вымышленного языка, похожего на ассемблер. Поскольку этот компилятор (названный hrm-compiler) выполняет некоторые оптимизации кода, я хотел знать, не нарушают ли оптимизации скомпилированный код. Чтобы протестировать их, я написал небольшой интерпретатор (hrm-interpreter) для запуска JSON-версии скомпилированного кода, а затем встроил версию веб-сборки в базовый отладчик, который я написал как веб-приложение.

Если вы хотите попробовать, лучше всего начать с Rust and Webassembly Book, учебника, который позволит вам написать крейт Rust, который может взаимодействовать с Javascript, и опубликовать его в npm. Я следовал инструкциям и сумел создать wasm-оболочку hrm-interpreter, названную (такая фантазия! Удивительно!) hrm-interpreter-wasm. Позвольте мне объяснить, что я узнал, создавая его.

Напишите другой крейт для поддержки wasm

Если вам нужно преобразовать типы Javascript в типы Rust, я бы предложил написать специальный крейт. Лучше думать об этом так, как будто вы пишете обертку. Этот новый крейт позволяет вам писать специфичный для wasm код без изменения вашей библиотеки для поддержки этой новой платформы.

Однако не каждой библиотеке нужно создавать специальный крейт: handlebars-rust предлагает пользователям флаг для отключения ввода-вывода на основе файловой системы. Это может сработать, если ваша основная библиотека не предоставляет сложные типы или вы уже работаете со специфическими для платформы вещами.

Не форсируйте файловый ввод-вывод в основной библиотеке

Что делает переводчик?

  1. прочитать файл
  2. преобразовать его содержимое в список операций
  3. выполнять каждую операцию до тех пор, пока программа не завершится или не возникнет какая-либо ошибка
  4. записывает вывод программы в файл (или терминал)

К сожалению, вы не можете читать или записывать файлы в среде wasm — вы не можете выполнять ввод-вывод! Поэтому нам нужно найти другой способ: давайте снова посмотрим на список.

  1. прочитать файл и извлечь его текст в виде большой строки
  2. преобразовать содержимое строки в список операций
  3. выполнять каждую операцию до тех пор, пока программа не завершится или не возникнет какая-либо ошибка
  4. записывает вывод программы в строку
  5. записывает выходную строку в файл (или терминал)

Таким образом, мы можем заметить, что фокус теперь находится на строках, а не на файлах.

Поскольку javascript может выполнять ввод-вывод (читать файлы через FileReader, генерировать новые файлы через FileWriter или даже выполнять HTTP-запросы!), наш крейт-оболочка может просто взять строку, передать ее в основной крейт и вернуть выходную строку. В моем случае мне пришлось создать несколько новых методов, чтобы обойти ввод-вывод из файлов.

Есть и другие случаи, когда вам может понадобиться модифицировать ваш крейт, чтобы портировать его на wasm: я предлагаю прочитать Какие крейты работают с Wasm? Узнать больше.

старайтесь показывать как можно меньше состояний — вместо этого используйте функции

Javascript не может манипулировать сложными вещами, такими как параметры, поэтому вы не можете экспортировать все, что хотите. Прежде чем тратить несколько часов на то, чтобы понять, почему вы не можете экспортировать свою красивую структуру с помощью вашего потрясающего Option‹Result‹Beatiful‹Type›››, ознакомьтесь с главой документации wasm-bindgen’s Supported Types. Поверьте мне :).

Чтобы обойти это, в hrm-interpreter-wasm…

  • основная структура не имеет публичных полей
  • внутреннее состояние сериализуется в строку в формате JSON
  • данные из Javascript также являются строками в формате JSON.

Это не лучший интерфейс, но пока работает. "Сейчас" означает, что я исправлю это, как только интегрирую некоторые незавершенные вещи, такие как новый компилятор на основе Rust и поддержка исходных карт… Если можете, постарайтесь избегать сериализации, т.к. это довольно дорого… и мы используем Rust в браузере, чтобы код выполнялся быстрее! ;)

Если вы хотите протестировать свой новый ящик в реальном приложении, вы можете использовать webpack. Начиная с версии 4, Webpack поставляет внутренний загрузчик wasm: он упрощает интеграцию библиотек на основе wasm!

На стороне браузера: используйте веб-пакет 4!

react-create-app предлагает очень простой способ начать работу. Если у вас более старый проект, я рекомендую вам переключиться на react-scripts ^2.1.1 и webpack ^4.26. Я не уверен, что вам нужно делать это в новых проектах, но мне пришлось «выбросить» конфигурацию веб-пакета (webpack.config.dev.js) и исключить файлы «.wasm» из расширений, управляемых «файловым» загрузчиком.

// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
  // Exclude `js` files to keep "css" loader working as it injects
  // its runtime that would otherwise be processed through "file" loader.
  // Also exclude `html` and `json` extensions so they get processed
  // by webpacks internal loaders.
  // PLEASE NOTE THE .wasm REGEX HERE!!!
  exclude:  [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/, /\.wasm$/],
  loader: require.resolve('file-loader'),
  options: {
    name: 'static/media/[name].[hash:8].[ext]',
  },
}

Если вы нашли эти заметки полезными или хотите предложить что-то еще, сообщите мне Твиттер или рассмотрите возможность предложить мне Ко-фи!