How do you read an array that was set in wasm from JS?

Viewed 96

I have a simple setArray.cpp file as follows:

int* x = new int[3];

extern "C" int* setArray() {
  
  x[0] = 11;
  x[1] = 22;
  x[2] = 33;
  return x;

}

And I compiled it and exported the function as follows:

em++ -O1 setArray.cpp -o setArray.js -s EXPORTED_FUNCTIONS='["_setArray"]'

In my JS code, I try calling setArray() expecting to get back the address of array x, and subsequently read its contents, but I get nothing but zeros when I read the memory.

Here are the contents of test-setArray.js:

const fs = require('fs');

const memory = new WebAssembly.Memory({
    initial: 1,
    maximum: 1
});
const heap = new Uint8Array(memory.buffer);

const imports = {
    env: {
        memory: memory,
        DYNAMICTOP_PTR: 4096,
        abort: function(err) {
            throw new Error('abort ' + err);
        },
        __cxa_throw: function(ptr, type, destructor) {
            console.error('cxa_throw: throwing an exception, ' + [ptr,type,destructor]);
        },
        __cxa_allocate_exception: function(size) {
            console.error('cxa_allocate_exception' + size);
            return false; // always fail
        },
        _emscripten_get_heap_size: function() {
            return heap.length;
        },
        emscripten_resize_heap: function(size) {
            return false; // always fail
        },
        emscripten_memcpy_big: function(dest, src, count) {
            heap.set(heap.subarray(src, src + count), dest);
        }
    }
};

const wasmSource = new Uint8Array(fs.readFileSync("setArray.wasm"));
const wasmModule = new WebAssembly.Module(wasmSource);
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);

function setArray() {
    const loc = wasmInstance.exports.setArray();
    console.log(loc, heap[loc], heap[loc+1], heap[loc+2]);
}

setArray();

The console.log statement outputs:

0 0 0 0

I would expect to see 11, 22, 33 printed. It feels like I'm missing something obvious. Any idea what it could be?

1 Answers

OK I found the answer: I was missing 3 Emscripten flags:

  • -Wl,--import-memory tells the host env (JS) to import the memory (as opposed to exporting it by default)
  • -s TOTAL_MEMORY=6MB sets the initial memory that will be used
  • [Optional] -s ALLOW_MEMORY_GROWTH=1 allows growing the memory from JS in runtime
Related