Правильный способ загрузки модуля WASM в React для больших файлов (более 4 КБ) - PullRequest
1 голос
/ 23 февраля 2020

Я застрял с тем, как загрузить функцию C ++ в код React, используя компилятор wasm.

Мой C ++ состоит из двух файлов, которые получаются после компиляции в файле wasm размером 160 КБ. Вот команда, которую я сейчас использую для компиляции (работающей в macOS).

em++ ../main.cpp ../stringFormat.cpp -s WASM=1 -s EXPORT_ALL=1 -s MODULARIZE=1 -O3 --closure 1 -o lss.js -std=c++11

Затем я копирую файл lss и wasm в мой код React в одну папку.

src
  - utils
    - wasm
      lss.js
      lss.wasm

Но тогда, когда я пытаюсь импортировать lss. js в другой файл, мое приложение вылетает с кучей неопределенных выражений.

Мой js файл

import * as lss from '../wasm/lss'
./src/utils/wasm/lss.js
  Line 10:6:     Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 11:69:    Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 11:117:   'read' is not defined                                                  no-undef
  Line 11:197:   'readbuffer' is not defined                                            no-undef
  Line 11:214:   'read' is not defined                                                  no-undef
  Line 11:336:   'quit' is not defined                                                  no-undef
  Line 11:367:   Unexpected use of 'print'                                              no-restricted-globals
  Line 11:430:   Unexpected use of 'print'                                              no-restricted-globals
  Line 11:493:   'printErr' is not defined                                              no-undef
  Line 12:1:     Unexpected use of 'print'                                              no-restricted-globals
  Line 12:22:    Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 12:26:    Unexpected use of 'self'                                               no-restricted-globals
  Line 14:307:   Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 23:174:   Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 29:10:    Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 29:152:   'readline' is not defined                                              no-undef
  Line 29:260:   Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 29:350:   Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 29:433:   Expected an assignment or function call and instead saw an expression  no-unused-expressions
  Line 30:19:    Expected an assignment or function call and instead saw an expression  no-unused-expressions
...

Я также попытался сгенерировать автономный файл wasm, добавив флаг SIDE_MODULE = 1 при компиляции.

// My util function to load wasm file to js
const loadWebAssembly = (filename, imports = {}) =>
    WebAssembly
        .instantiateStreaming(fetch(filename), imports)
        .then(({instance}) => instance.exports);

// wasm file is now in my static folder, in public/
const lss = loadWebAssembly('wasm/lss.wasm')
    // splitIntoCommonSequences is the C++ function I try to call
    .then(module => Promise.resolve(module.splitIntoCommonSequences));

export {lss};

Но затем я получил еще одну ошибку.

WebAssembly.instantiate(): Import #0 module="env" error: module is not an object or function

Я попытался выяснить, как объявить правильный объект импорта для моей ситуации без какого-либо успеха. Их лучшее решение? Как мне узнать, что поместить в мой объект импорта?

Спасибо!

1 Ответ

1 голос
/ 07 марта 2020

Итак, я выяснил, что они должны были сделать больше шагов, чтобы заставить двоичный файл WASM работать с React, особенно если вы хотите импортировать его в модули React es6 (не из папки publi c).

Я не знаю Не претендуйте на это как на общее решение, только то, которое работает для меня (и, кажется, работает в большинстве случаев).

ФЛАГИ КОМПИЛЕРА

Вот em ++ Команда build, которую я сейчас использую:

em++ ../main.cpp ../stringFormat.cpp \
      -Os -g1 \
      -s WASM=1 \
      -s MALLOC=emmalloc \
      -s ALLOW_MEMORY_GROWTH=1 \
      -s EXPORT_ES6=1 \
      -s MODULARIZE=1 \
      -s 'EXPORT_NAME="LongerSubSequence"' \
      -s 'ENVIRONMENT="web"' \
      --bind \
      -o lss.mjs \
      -std=c++11 || exit 1

Не уверен насчет всех опций, здесь может быть какая-то оптимизация. Важными из них являются:

  • Bind : очевидно, необходимо, чтобы компилятор переопределил искажение имен C ++ при именовании модулей js (по умолчанию компилятор C ++ изменяет все имена переменных вида как реагирует с модулями css).
  • EXPORT_ES6 : слегка изменит синтаксис сгенерированного клея js, чтобы его можно было импортировать способом es6.
  • MODULARIZE : экспортировать все функции под общим модулем (с именем EXPORT_NAME flag), так что вы можете просто импортировать js как модуль и вызывать Module.My_CPP_Function в вашем коде js .

  • g1 : Сохраняйте сгенерированный код клея достаточно читабельным для управления следующим шагом.

ДОБАВЛЕНИЕ ФАЙЛОВ В REACT

В процессе создаются два файла: lss.m js и lss.wasm. Они go как в дереве проекта React:

My_React_Project
  |_public
  | |_/path/to/lss.wasm
  |
  |_src
    |_/path/to/lss.mjs

/ path / to / могут быть любым путем внутри папки, даже root.

ADAPT КЛЕЙ JS

Наконец, чтобы исправить ошибки, я отредактировал сгенерированный файл lss.m js:

  1. Добавить /* eslint-disable */ вверху файла, во избежание синтаксических ошибок React.
  2. Замените var _scriptDir = import.meta.url; на var _scriptDir = '/path/to/lss.wasm'; относительно папки publi c. Не уверен, что это полезно для следующих шагов, но React просто создаст sh с синтаксисом import.meta.
  3. Замените scriptDirectory = self.location.href; на scriptDirectory = window.self.location.href;, поскольку функции React es6 не связаны с window.
  4. Удалите следующий блок:
var dataURIPrefix = "data:application/octet-stream;base64,";

function isDataURI(filename) {
 return String.prototype.startsWith ? filename.startsWith(dataURIPrefix) : filename.indexOf(dataURIPrefix) === 0;
}

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

заменить var wasmBinaryFile = "lss.wasm"; на const wasmBinaryFile = '/path/to/lss.wasm'; ('/' будет указывать на папку publi c). Удалить:
if (!isDataURI(wasmBinaryFile)) {
 wasmBinaryFile = locateFile(wasmBinaryFile);
}
Удалить:
function getBinary() {
 try {
  if (wasmBinary) {
   return new Uint8Array(wasmBinary);
  }
  if (readBinary) {
   return readBinary(wasmBinaryFile);
  } else {
   throw "both async and sync fetching of the wasm failed";
  }
 } catch (err) {
  abort(err);
 }
}
Заменить функцию getBinaryPromise() на следующую:
const getBinaryPromise = () => new Promise((resolve, reject) => {
 fetch(wasmBinaryFile, { credentials: 'same-origin' })
   .then(
     response => {
      if (!response['ok']) {
       throw "failed to load wasm binary file at '" + wasmBinaryFile + "'";
      }
      return response['arrayBuffer']();
     }
   )
   .then(resolve)
   .catch(reject);
});
Несколько строк ниже, замените
if (!wasmBinary && typeof WebAssembly.instantiateStreaming === "function" && !isDataURI(wasmBinaryFile) && typeof fetch === "function")

на

if (!wasmBinary && typeof WebAssembly.instantiateStreaming === "function" && typeof fetch === "function")

Это просто удалит условие isDataURI, так как мы «доверяем» пути, который мы дали нашему двоичному файлу файл.

СЕЙЧАС ИСПОЛЬЗУЙТЕ СВОЙ БИНАРНЫЙ КОД

И это все, что мне понадобилось, чтобы справиться с этим. Теперь в любом из моих реагирующих файлов я могу просто использовать свою функцию c ++ следующим образом:

anyfile. js

import LongerSubSequenceGlue from 'path/to/lss.mjs';
// Avoid main function running if you just want to use another function
const lssModule = LongerSubSequenceGlue({
    noInitialRun: true,
    noExitRuntime: true
});

//...

lssModule.my_cpp_function(...my_params);

Работает как шарм на Chrome, Firefox и Safari сделают еще тест позже.

...