5

At present, Webassembly only supports a handful of parameter types, namely fixed sized integers and floating point numbers. This means that I can only define and export functions from my C/Rust modules that accept and return numeric values.

However, according to the Mozilla Developer Network, I can manipulate the module's memory from the host Javascript:

[M]emory created by JavaScript or in WebAssembly code will be accessible and mutable from both JavaScript and WebAssembly.

This sounds very promising--it indicates that I could designate part of the memory as a byte buffer in which to shuttle more complex data back and forth across the language barrier. Functions in my module could accept and return pointers (which are themselves i32, fixed size integers), thereby working within the current constraints.

Unfortunately, it is not clear how I should go about managing this memory. If I need to pass data to the Wasm process from JS, I need to write to the Memory object directly but won't know which regions in the Memory are free.

What's the safest strategy? Should I export a pair of malloc-and-free-style functions that give the JS a way to request memory before calls into Wasm? Or is there an established best practice?

Andreas Rossberg
  • 34,518
  • 3
  • 61
  • 72
zslayton
  • 51,416
  • 9
  • 35
  • 50

1 Answers1

4

I think the easiest thing is to use Emscripten, and use its built-in malloc / free. Then export a function which, in C++, allocates the memory requested through that malloc / free, and returns the pointer. That way JavaScript can call into WebAssembly to get a usable memory region which isn't already used.

I've detailed how to share strings to / from JS / wasm in this answer, which has details on some of the above.

Note that pointers in WebAssembly aren't really a thing. C++ simply maps them to the Memory, which starts at 0. So when you index the ArrayBuffer you just need the pointer from C++, no extra mapping required.

Community
  • 1
  • 1
JF Bastien
  • 6,673
  • 2
  • 26
  • 34
  • That makes sense, thanks! I notice that emscripten's `malloc` implementation can trigger the `grow_memory` operation. Do you happen to know if that would invalidate any pointers already issued? Or does that process preserve the original memory layout? – zslayton May 21 '17 at 02:11
  • 1
    Though you can configure Emscripten to never grow, always preallocate and fail if it goes OOM. – JF Bastien May 21 '17 at 02:13
  • 1
    How does one discover that an ArrayBuffer has been invalidated? Does accessing it in JS cause an exception? (Or have I misunderstood your use of 'neuter'?) – zslayton May 21 '17 at 02:26
  • 1
    Sorry, detach is what I should have said. As soon as it succeeds in growing it'll do that. I don't believe Emscripen will tell you though! The `byteLength` will become `0` on detach. WebAssembly details on this: https://github.com/WebAssembly/design/blob/master/JS.md#webassemblymemoryprototypegrow – JF Bastien May 21 '17 at 02:32
  • Thanks for the docs, very helpful. It sounds as though the ArrayBuffer itself will be truncated to a length of zero, but any makeshift `i32` pointers I have would still be valid if used on the replacement buffer. – zslayton May 21 '17 at 02:38
  • 2
    Correct, the pointer are still valid because they are indices into the Memory / ArrayBuffer. The underlying virtual memory may move, but that's invisible from WebAssembly / JavaScript. – JF Bastien May 21 '17 at 03:18