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

Tito picture Tito · Nov 12, 2017 · Viewed 8k times · Source

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.

Answer

Clint picture Clint · Apr 4, 2018

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