Explanation
The compiled WebAssembly module, with the function _stringlen()
inside, uses its memory expressed as a linear memory. The compiled function only knows the memory of its module. Therefore, the pointer char*
must point to a chunk of the module's memory.
Quick solution
We will keep the C code intact, except for adding the missing include:
#include <string.h>
int stringlen(char *p)
{
return strlen(p);
};
The output JavaScript code example.js
contains functions for memory management and more. Next, in the test.js
script, we:
- Convert our string to raw bytes (into
UInt8Array
)
- Allocate a chunk of the module's memory for the string (no need for
\0
character at the end)
- (A) Access the allocated chunk of the module's memory and (B) fill it with our raw string (we access the memory as it represents single unsigned bytes
HEAPU8
)
- Free the allocated chunk
const Module = require('./example.js');
const str_raw = new TextEncoder().encode('Lorem ipsum'); // 1)
let ptr = Module._malloc(str_raw.length); // 2)
let chunk = Module.HEAPU8.subarray(ptr, ptr + str_raw.length); // 3A)
chunk.set(str_raw); // 3B)
console.log(Module._stringlen(chunk.byteOffset));
Module._free(ptr); // 4)
For the compilation, I have forced a synchronised compilation of the WebAssembly module (-s WASM_ASYNC_COMPILATION=0
) to make sure the module will be ready after require()
:
emcc -s EXPORTED_FUNCTIONS="['_stringlen']" example.c -o example.js -s WASM_ASYNC_COMPILATION=0
We execute the script as follows node test.js
.
Other solutions
My favourite solution is the already mentioned embind. It requires you to use C++:
#include <string>
#include <emscripten.h>
#include <emscripten/bind.h>
size_t stringlen(std::string p)
{
return p.size();
};
EMSCRIPTEN_BINDINGS(Module)
{
emscripten::function("stringlen", &stringlen);
}
We compile the code by indicating the use of embind with --bind
flag:
emcc example.cpp -o example.js -s WASM_ASYNC_COMPILATION=0 -std=c++11 --bind
The function call is much simpler now:
const Module = require('./example.js');
console.log(Module.stringlen('Lorem ipsum'))