[Начало работы с WebAssembly] Связывание с Webpack

JavaScript WebAssembly Rust Parcel Webpack

title: [Начало работы с WebAssembly] Связывание с Webpack

date: 2018-4-6 19:40:00

категории: WebAssembly, Заметки

tags: WebAssembly, JavaScript, Rust, LLVM toolchain

auther: Yiniau


Связь с вебпаком


Очень медленно рутинно писать ржавый код, а затем вручную компилировать его в файл wasm.В настоящее время есть несколько решений.Далее я улучшу эффективность написания WebAssembly на основе webpack.

первый,webpack 4Требуется, версия на момент написания этой статьиwebpack 4.5.0

Конкретная установка вебпака решается сама собой

настроить веб-пакет

Создаватьwebpack.config.js, введите следующий код

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

const paths = {
  src: path.resolve(__dirname, 'src'),
  entryFile: path.resolve(__dirname, 'src', 'index.js'),
  dist: path.resolve(__dirname, 'dist'),
  wasm: path.relative(__dirname, 'build'),
}

module.exports = {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: __dirname,
    hot: true,
    port: 10001,
    open: true, // will open on browser after started
  },
  entry: paths.entryFile,
  output: {
    path: paths.dist,
    filename: 'main.js'
  },
  resolve: {
    alias: {
      wasm: paths.wasm,
    }
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: [{
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
          }
        }],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'WebAssembly Hello World'
    }),
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin(),
  ],
};

Интерпретация файла конфигурации не будет выполняться. Это очень простая конфигурация. Если вы хотите изучить расширенную настройку, вы можете посмотретьcreate-react-appФайл конфигурации веб-пакета из извлечения

настроить бабел

.babelrc

{
  "presets": [
    "env"
  ],
  "plugins": [
    "syntax-dynamic-import",
    "syntax-async-functions"
  ]
}

Это здесьasyncподдержка иimport()Поддержка динамического импорта

Следует отметить, что статический импорт импортирует.wasmФайл не поддерживается встроенным веб-пакетом, веб-пакет напечатает сообщение об ошибке в консоли, предлагая вам перейти на динамический импорт (Динамический импорт)

Не добавлять поддержку загрузчика сообщества

webpack 4 имеет встроенную поддержку парсинга.wasmфайл, а импорт имеет наивысший приоритет поиска, когда не пишется суффикс

Давайте сначала посмотрим на каталог

𝝪 yiniau @ yiniau in /Users/yiniau/code/WebAssembly/hello_world
↪ ll
total 952
-rw-r--r--    1 yiniau  staff    52B  3 25 22:14 Cargo.lock
-rw-r--r--    1 yiniau  staff   143B  4  6 21:51 Cargo.toml
drwxr-xr-x    4 yiniau  staff   128B  4  6 01:36 build
-rw-r--r--    1 yiniau  staff    12B  3 26 21:53 build.sh
-rw-r--r--    1 yiniau  staff   170B  4  4 16:26 index.html
drwxr-xr-x  809 yiniau  staff    25K  4  6 20:34 node_modules
-rw-r--r--    1 yiniau  staff   782B  4  6 20:34 package.json
drwxr-xr-x    3 yiniau  staff    96B  4  4 16:41 rust
drwxr-xr-x    4 yiniau  staff   128B  4  4 17:16 src
drwxr-xr-x    5 yiniau  staff   160B  3 29 15:29 target
-rw-r--r--    1 yiniau  staff   1.2K  4  6 15:51 webpack.config.js
-rw-r--r--    1 yiniau  staff   230K  4  6 01:07 yarn-error.log
-rw-r--r--    1 yiniau  staff   216K  4  6 16:09 yarn.lock

в

  • rustХранится в.rsдокумент
  • srcХранится в.jsдокумент
  • buildХранится в.wasmдокумент
  • index.jsФайл записи, указанный для записи, здесь я ввожу полифилл
𝝪 yiniau @ yiniau in /Users/yiniau/code/WebAssembly/hello_world
↪ ll src
total 16
-rw-r--r--  1 yiniau  staff    77B  4  6 20:39 index.js
-rw-r--r--  1 yiniau  staff   1.9K  4  6 21:30 main.js

хорошо, давайтеmain.jsЗавершите основную логику в

main.js

(async () => {
  import('../build/hello.wasm')
    .then(bytes => bytes.arrayBuffer())
    .then(res => WebAssembly.instantiate(bytes, imports))
    .then(results => {
      console.log(results);
      const exports = results.instance.exports;
      console.log(exports);
      mem = exports.memory;
    });
})()

о черт!!Почему я получаю сообщение об ошибке! !

Все в порядке, сообщение об ошибке ясно

WebAssembly.Instance is disallowed on the main thread, if the buffer size is larger than 4KB. Use WebAssembly.instantiate.

Если размер буфера превышает4KB,WebAssembly.InstanceЗапрещено в основном потоке. Нужно использоватьWebAssembly.instantiateНа месте, но проблема пришла.

import()ImportSObject не может быть передан. Давайте посмотрим на проблемы на GitHub WebPack:

github issue

linclark (автор мультфильма к WebAssembly) предлагает использоватьinstantiateStreamingзаменятьcompileStreaming, чтобы избежать влияния ограничений быстрой памяти на ios.

Сокра немного против этого (должна быть очень против!)

Причина не поддерживается

Предварительная информация

webpack пытается рассматривать WASM как ESM. Примените все правила/допущения, применимые к ESM, и к WASM. Гипотетически в будущем WASM JS API может быть интегрирован WASM в граф модуля ESM.

Это означает, что в файле WASMimportsРаздел (importsObject) по сравнению с ESMimportразбираются как предложения,exportsРазделы (instance.exports) обрабатываются как в ESMexportчасть.

Модуль WASM также имеетstartраздел, который выполняется при создании экземпляра WASM.

В WASM импорт JS API проходит черезimportsObjectПередано созданному модулю WASM.

Спецификация ЕСМ

Спецификация ESM определяет несколько этапов. СценаModuleEvaluation. На этом этапе все модули оцениваются в четком порядке. Эта фаза является синхронной. Все модули оцениваются с одной и той же «галочкой».

Когда WASM находится на блок-диаграмме, это означает:

  • startДетали выполнены в одном "галочке"
  • Все зависимости WASM выполняются в один и тот же «тик»
  • ESM, импортированный в WASM, выполняется в том же «тике»

Для использования промисовinstantiate, такое поведение невозможно. Обещание всегда откладывает свое выполнение до другого «тика».

Это возможно только при использовании созданной синхронной версии (WebAssembly.Instance).

Примечание. Технически можетstartЧастично и нет зависимости WASM. В данном случае это неприменимо. Но мы не можем думать, что ситуация всегда.

webpack хочет загружать/компилировать файлы wasm параллельно с загрузкой кода JS. использоватьinstantiateStreamingне допустит этого (когда WASM имеет зависимости), потому что создание экземпляра должно пройтиimportsObject. СоздайтеimportsObjectВсе зависимости/импорты WASM необходимо оценить, поэтому эти зависимости необходимо загрузить перед началом загрузки WASM.

когда используешьcompileStreaming + new WebAssembly.InstanceПараллельная загрузка и компиляция возможны, потому чтоcompileStreamingне нужен одинimportsObject. Может быть создан после завершения загрузки WASM и JS.importsObject.

Спецификация WebAssembly

Я также хотел бы обратиться к спецификации WebAssembly. В нем говорится, что компиляция происходит в фоновом потокеcompileи создание экземпляра происходит в основном потоке.

Прямо об этом не сказано, но мне кажется, что поведение АО нестандартно.

другие инструкции

В WASM также отсутствует возможность использовать живые привязки импортированных идентификаторов. Напротив,importsObjectбудет скопировано. Это может привести к странным циклическим зависимостям и проблемам с WASM.

существуетimportsObjectсредняя поддержкаgetterи может выполнитьstartБыло бы лучше получить экспорт раньше некоторых.

Попробуйте использовать загрузчик для прямого анализа.rs

wasm-loaderЕсть проблема с поддержкой файлов .wasm, скомпилированных напрямую rustup wasm32-unknown-unknown.Я видел, что wasm-loader использует файлы wasm на основе тулчейна emcc.Я пытался использовать его напрямую

rules: [
  {
    test: /\.(js|jsx)$/,
    exclude: /node_modules/,
    use: [{
      loader: 'babel-loader',
      options: {
        cacheDirectory: true,
      }
    }],
  },
  {
    test: /\.wasm$/,
    include: path.resolve(__dirname, 'wasm'),
    use: 'wasm-loader',
  },
],

Но выдаст ошибку:

Это должно быть проблемой кодирования, созданной набором инструментов

Поэтому я попытался снова, используяrust-native-wasm-loader:

webpack.config.js

rules: [
  {
    test: /\.(js|jsx)$/,
    exclude: /node_modules/,
    use: [{
      loader: 'babel-loader',
      options: {
        cacheDirectory: true,
      }
    }],
  },
  {
    test: /\.rs$/,
    include: paths.rust,
    use: [{
      loader: 'wasm-loader'
    }, {
      loader: 'rust-native-wasm-loader',
      options: {
        release: true,
      },
    },]
  },
],

rust/add.rs

#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
    eprintln!("add({:?}, {:?}) was called", a, b);
    a + b
}

main.js

import loadAdd from 'rust/add.rs';

loadAdd().then(result => {
  const add = result.instance.exports['add'];
  console.log('return value was', add(2, 3));
});

BUT!

Чен Чен не пришел! !

Я его полностью переделал по примеру rust-native-wasm-loader, но похоже, что нынешние плагины все остались с эпохи asm.js, и все они разобраны в.wasmНе удается ли выполнить этот шаг, поскольку WebAssembly не подходит для синхронного вызова? . На данный момент, если вы вызовете ржавчинуstd::memДля работы с объектами памяти размер файла будет очень большим — используйтеwasm-gcОсталось еще более 200 КБ после

#![feature(custom_attribute)]
#![feature(wasm_import_memory)]
#![wasm_import_memory]

use std::mem;
use std::ffi::{CString, CStr};
use std::os::raw::{c_char, c_void};

/// alloc memory
#[no_mangle]
// In order to work with the memory we expose (de)allocation methods
pub extern fn alloc(size: usize) -> *mut c_void {
    let mut buf = Vec::with_capacity(size);
    let ptr = buf.as_mut_ptr();
    mem::forget(buf);
    ptr as *mut c_void
}

Возможно, практика веб-пакета не подходит для всех режимов применения веб-сборки, и она обрабатывается по пути ESM..wasmЭто кажется очень хорошим, но фактическое использование может быть проблемой.В настоящее время это в основном логика обработки js.Чтобы быть совместимым с браузерами более низких версий, асинхронная обработка (возможно) необходима?

2018-4-17 12:00 Обновить

Пакет!

Обновился PR поддержки webpack для webassembly! Скромный советI don't know if this helps but it seems parceljs has got support for rust functions. BY pyros2097 https://medium.com/@devongovett/parcel-v1-5-0-released-source-maps-webassembly-rust-and-more-3a6385e43b95

ok

Я еду в Посылку...

Хотя импорт нельзя передать, его также можно использовать для однофункциональной разработки.