В моей предыдущей статье мы рассмотрели идею создания простого модуля WebAssembly без использования Emscripten. В этой статье мы смогли вызвать модуль из JavaScript, но не сделали обратного:

Как вызвать JavaScript из модуля?

Вызов JavaScript из модуля WebAssembly

В этой статье мы попытаемся заставить модуль WebAssembly вызывать метод, определенный в нашем JavaScript.

Чтобы скомпилировать код C, когда используемый метод отсутствует в исходном коде, вам необходимо определить сигнатуру метода с помощью ключевого слова extern, как показано в следующем примере:

// Define the JavaScript method's signature that we're going to 
// be calling.   
extern void CallJS(int iVal);    

// A method that the JavaScript will call into to trigger our code   
// that will in turn call a JavaScript method passing along the 
// value received.   
void Test(int iVal){ CallJS(iVal); }

Вы можете запустить следующую командную строку для создания файла wasm:

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm

Чтобы определить метод в нашем JavaScript, нам просто нужно добавить его к объекту env, являющемуся частью основного объекта, который мы передаем в качестве второго параметра методу WebAssembly.instantiate:

var importObject = {
  'env': {
    // ... (other properties/methods of this object)
    '_CallJS': function(iVal){  alert("value received: " + iVal.toString()); }
  }
};

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

Ниже приведен полный код HTML/JavaScript для нашего примера модуля:

<!DOCTYPE html>   
<html>
  <head>
    <meta charset="utf-8"/>
  </head>     
  <body>
    <input type="text" id="txtValue" />       
    <input type="button" value="Pass Value" onclick="PassValue();" />
    <script type="text/javascript">
      var gModule = null;
      var importObject = {
        'env': {
          'memoryBase': 0,
          'tableBase': 0,
          'memory': new WebAssembly.Memory({initial: 256}),
          'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'}),
          '_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
        }
      };          

      fetch('test.wasm').then(response =>
        response.arrayBuffer()
      ).then(bytes =>
        WebAssembly.instantiate(bytes, importObject)
      ).then(results => {
        // Hold onto the module's instance so that we can reuse it
        gModule = results.instance;
      });           

      function PassValue(){
        // Get the value from the textbox (convert the value from
        // a string to an int)
        var iVal = parseInt(document.getElementById("txtValue").value,10);            

        // Call the method in the module           
        gModule.exports._Test(iVal);
      }
    </script>
  </body>
</html>