Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebAssembly calling JavaScript methods from wasm i.e. within C++ code

I was playing with WebAssembly and so far and I was able to manage emscripten compile my test C++ project to wasm file em++ provides me 2 files i.e.

mainTest.js mainTest.wasm

When I load mainTest.js in my html page then I get a JavaScript object called "Module".

I did found how to call C++/wasm methods from javascript i.e. something like:

var myTestInteger = Module._callMyTestMethod();

and read strings from the Module.wasmMemory.buffer , but I do NOT understand how to call JavaScript from C++ code.

i.e. I would like to be able to do something like that:

#ifdef __cplusplus
extern "C" {
#endif
extern void testExternJSMethod();

int main() 
{
    cout << " Hello From my Test1 !" << endl;

    testExternJSMethod();
    return 0;
}
int EMSCRIPTEN_KEEPALIVE callMyTestMethod(){
    return 26;
}
#ifdef __cplusplus
}
#endif

and the my js method testExternMethod that I am loading in another js file called utils.js

function testExternMethod() {
  console.log("Hello from testExternMethod!" + )
}

Here I would like to call the JavaScript testExternJSMethod from C++.

When I run the page in Firefox get "-1" in the debugger console.

So what I am missing in this case? Unfortunately The Mozilla documentation is giving only examples in those S-expressions instead of C++.

What am I missing in example? In C++ I have defined the method with the extern keyword i.e.

extern void testExternJSMethod();

but I get the feeling that that is not all I have to do.

I believe that I should somehow link that JavaScript method to the Module somehow but I do not know how. Module.asm gives me the exports. Which method call should give me the imports? since I believe that this _testExternJSMethod() should be in some imports method I can not figure out how to get to it.

like image 530
Tito Avatar asked Nov 12 '17 11:11

Tito


People also ask

Can C compile to Wasm?

When you've written a new code module in a language like C/C++, you can compile it into WebAssembly using a tool like Emscripten.

Can JavaScript be compiled to WebAssembly?

Yes, it is totally possible to call JavaScript-functions from inside your running WebAssembly functions!

Does WebAssembly use JavaScript?

WebAssembly modules can be imported into a web (or Node. js) app, exposing WebAssembly functions for use via JavaScript. JavaScript frameworks could make use of WebAssembly to confer massive performance advantages and new features while still making functionality easily available to web developers.

Can Wasm interact with Dom?

"Unfortunately, the DOM can only be accessed within the browser's main JavaScript thread" - while that is true, it is not the reason why WebAssembly cannot interact with the DOM.


2 Answers

I'm not exactly sure of your use case, but you are missing important steps to be able to use your function testExternalJSMethod. You have two options here:

Option 1 - Library

1 - Define your function in c/c++ .

extern void testExternalJSMethod();

2 - Create a file called myLibrary.js

3 - The JS function needs to be added to the LibraryManager in your library file with the following code:

function makeAlert(text) {
    alert(text);
}

if (typeof mergeInto !== 'undefined') mergeInto(LibraryManager.library, {
    testExternalJSMethod: function() {
        makeAlert("Hello world");
    }
});

4 - If testExternalJSMethod depends on anything outside of its own scope (for example, makeAlert above), make sure to include the script in your html page

<script async type="text/javascript" src="myLibrary.js"></script>

5 - Add option --js-library to your emcc command, and immediately after the relative path to myLibrary.js

emcc ... --js-library myLibrary.js

Option 2 - Passing Pointers

1 - Define your javascript function type in c/c++

typedef void testExternalJSMethod()

2 - Wherever you want this function to be used, accept an int param which will be the function pointer, and cast the pointer to your function

void passFnPointer(int ptr) {
    ((testExternalJSMethod*)ptr)();
}

3 - Use emscripten's addFunction() and store its returned value (the pointer in c/c++)

var fnPtr = Module.addFunction(function () {
    alert("You called testExternalJSMethod");
});

4 - Use the stored pointer value from step 3 to pass to our function passFnPointer

var passFnPointer = Module.cwrap('passFnPointer', 'undefined', ['number']);
passFnPointer(fnPtr);

5 - Add option -s RESERVED_FUNCTION_POINTERS to your emcc command

emcc ... -s RESERVED_FUNCTION_POINTERS=10
like image 142
Clint Avatar answered Oct 29 '22 14:10

Clint


Have you tried looking at the Emscripten documentation? It has a whole section on interacting with code that details how to expose C / C++ functions to JavaScript and call JavaScript functions from C / C++.

Emscripten provides two main approaches for calling JavaScript from C/C++: running the script using emscripten_run_script() or writing “inline JavaScript”.

It is worth noting that the Mozilla documentation details plain WebAssembly, whereas Emscripten adds a lot more framework and tooling around WebAssembly in order to make it easier to port large C / C++ codebases.

like image 41
ColinE Avatar answered Oct 29 '22 13:10

ColinE