How to call a webassembly method in vue.js?

冷暖自知 提交于 2020-01-05 07:12:48

问题


I'm trying to transpose to vue.js this simple html page add.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
  </head>
  <body>
    <input type="button" value="Add" onclick="callAdd()" />

    <script>
      function callAdd() {
        const result = Module.ccall('Add',
            'number',
            ['number', 'number'],
            [1, 2]);

        console.log(`Result: ${result}`);
      }
    </script>
    <script src="js_plumbing.js"></script>
  </body>
</html>

which calls the Add function defined in add.c :

#include <stdlib.h>
#include <emscripten.h>

// If this is an Emscripten (WebAssembly) build then...
#ifdef __EMSCRIPTEN__
  #include <emscripten.h>
#endif

#ifdef __cplusplus
extern "C" { // So that the C++ compiler does not rename our function names
#endif

EMSCRIPTEN_KEEPALIVE
int Add(int value1, int value2) 
{
  return (value1 + value2); 
}

#ifdef __cplusplus
}
#endif

and converted to js_plumbing and js_plumbling.wasm files through the command:

emcc add.c -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -s 
ENVIRONMENT='web','worker'

In console of google chrome I get these errors:

GET http://localhost:8080/dist/js_plumbing.wasm 404 (Not Found)  @  js_plumbing.js?2b2c:1653

Where in js_plumbing_js :

// Prefer streaming instantiation if available.
  function instantiateAsync() {
    if (!wasmBinary &&
        typeof WebAssembly.instantiateStreaming === 'function' &&
        !isDataURI(wasmBinaryFile) &&
        typeof fetch === 'function') {
      fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) {  // <---------------!!!
        var result = WebAssembly.instantiateStreaming(response, info);
        return result.then(receiveInstantiatedSource, function(reason) {
            // We expect the most common failure cause to be a bad MIME type for the binary,
            // in which case falling back to ArrayBuffer instantiation should work.
            err('wasm streaming compile failed: ' + reason);
            err('falling back to ArrayBuffer instantiation');
            instantiateArrayBuffer(receiveInstantiatedSource);
          });
      });
    } else {
      return instantiateArrayBuffer(receiveInstantiatedSource);
    }
  }

In Google Chrome: createWasm @ js_plumbing.js?2b2c:1680

line 1680 of js_plumbing.js:

instantiateAsync();

in Google Chrome: eval @ js_plumbing.js?2b2c:1930

line 1930 of js_plumbing.js:

<pre><font color="#4E9A06">var</font> asm = createWasm();</pre>

And many other errors related to wasm :

So... how should I modify the callAdd() method in Result.vue in order to correctly execute the Add function in js_plumbing.js and in js_plumbing.wasm files?

  methods: {
    callAdd() {
      const result = Module.ccall('Add',
          'number',
          ['number', 'number'],
          [1, 2]);
      console.log('Result: ${result}');
    }
  }

Updates:

1 update)

I compiled the add.c with this command:

emcc add.c -o js_plumbing.mjs -s EXTRA_EXPORTED_RUNTIME_METHODS=
['ccall','cwrap'] -s ENVIRONMENT='web' . 

Then created a js_plumbing.js file :

. import wasm from './js_plumbing.mjs';

const instance = wasm({
  onRuntimeInitialized() {
    console.log(instance._addTwoNumbers(3,2));
  }
}) . 

Doing npm run dev:

Failed to compile.

./src/components/js_plumbing.mjs 3:25
Module parse failed: Unexpected token (3:25)
You may need an appropriate loader to handle this file type, currently 
no loaders are configured to process this file. 
See https://webpack.js.org/concepts#loaders
| 
| var Module = (function() {
>   var _scriptDir = import.meta.url;
|   
|   return (

Update 2)

I solved the 404 error by putting the wasm file into a /div subfolder within the same folder of the index.html file.

Now I’m facing this problem: “Cannot read property ‘ccall’ of undefined”

But I compiled the add.c file, creating js_plumbing.js and js_plumbing.wasm files, with this command, which exports the methods ‘ccall’ and ‘cwrap’ :

emcc add.c -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=[‘ccall’,‘cwrap’] -s ENVIRONMENT=‘web’,‘worker’

3° Update)

I "solved" through a sort of an hack, which I do not like at all.

This is the Result.vue file:

<template>
  <div>
    <p button @click="callAdd">Add!</p>
    <p>Result: {{ result }}</p>
  </div>
</template>

<script>
    import * as js_plumbing from './js_plumbing'
    import Module  from './js_plumbing'
    export default {
      data () {
        return {
          result: null
        }
      },
      methods: {
        callAdd () {
          const result = js_plumbing.Module.ccall('Add',
            'number',
            ['number', 'number'],
            [1, 2]);
          this.result = result;
        }
      }
    }
</script>

which is exactly the same as the one used before

The only thing I've done to make it working, is to add export to the definition of Module in js_plumbing.js :

js_plumbing.js

// Copyright 2010 The Emscripten Authors.  All rights reserved.
// Emscripten is available under two separate licenses, the MIT 
license and the
// University of Illinois/NCSA Open Source License.  Both these 
licenses can be
// found in the LICENSE file.

// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(Module) { ..generated code.. }
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of 
the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define   var Module = 
{};
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
export var Module = typeof Module !== 'undefined' ? Module : {};

But, as I said, I do not like this hack. Any suggestions on how to make the Module exportable, thus importable, without manually adding 'export' in js_plumbing.js file?

Marco


回答1:


First, the 404 error should be addressed. Does file /dist/js_plumbing.wasm exist? I've needed to copy .wasm files manually in the past because some automatic build systems (like Parcel) currently don't.

You can build with the MODULARIZE option to import into your build system.

addTwoNumbers.c

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int addTwoNumbers(int value1, int value2) 
{
  return (value1 + value2); 
}

build command

$ emcc -o dist/addTwoNumbers.js -s MODULARIZE=1 src/addTwoNumbers.c

Vue Implementation

import myMathModule from './js_plumbing';

let instance = {
  ready: new Promise(resolve => {
    myMathModule({
      onRuntimeInitialized() {
        instance = Object.assign(this, {
          ready: Promise.resolve()
        });
        resolve();
      }
    });
  })
};

export default {
  data () {
    return {
      result: null
    };
  },
  methods: {
    callAdd(a, b) {
      instance.ready
      .then(_ => this.result = instance._add(a, b));
    }
  }
};

Use the onRuntimeInitialized method to detect when the WASM module is ready. Your exported functions will have an underscore in front of them.

require() could possibly be used in place of import:

const wasmModule = require('./addTwoNumbers.js');

...


来源:https://stackoverflow.com/questions/59500326/how-to-call-a-webassembly-method-in-vue-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!