WebAssembly return string use C

How can I return a JavaScript string from a WebAssembly function?

https://dev.to/azure/passing-strings-from-c-to-javascript-in-web-assembly-1p01 - not working

C

#include <stdio.h>
#include <string.h>

void jsPrintString(const char *s, uint16_t len);

void print() {
  const char* str = "Hello from C++!";
  jsPrintString(str, strlen(str));
}

Compiler:

emcc -Os start.c  -s STANDALONE_WASM -s EXPORTED_FUNCTIONS="['_hello']" -s ERROR_ON_UNDEFINED_SYMBOLS=0 -Wl,--no-entry -o "test.wasm"

Javascript:

const memory = new WebAssembly.Memory({ initial: 1 });

    function handlePrintString (offset, length) {
      console.log(offset, length);
      const bytes = new Uint8Array(memory.buffer, offset, length);
      const string = new TextDecoder('utf8').decode(bytes);
      console.log(string); //empty ???????????????????????????????????
    }

    const importObject = {
      env: {
        jsPrintString: handlePrintString,
        memory: memory
      },

      js: { mem: memory }
    };

    WebAssembly.instantiateStreaming(fetch('/js/test.wasm'), importObject)
      .then(obj => {
        console.log(obj.instance.exports);
        console.log(obj.instance.exports.print());
      });

memory ArrayBuffer [0,0,0,0,0,0,0,0,0,0,0.....] ???

Upvotes: 4

Views: 2780

Answers (3)

Javier Segura
Javier Segura

Reputation: 1

The best solution is the one from Iron Toad because actually responds to the initial question. I don't understand why no one vote up.

Only in the example, if you try to do in Typescript is better to change String.fromCharCode.apply(null, charArray) to TextDecoder('utf8').decode(charArray) so you don't get type warnings with the ts compiler

Upvotes: 0

Iron Toad Tough
Iron Toad Tough

Reputation: 113

You're nearly there with your code, it is possible to do it as you imply. You don't have to explicitly copy to shared memory as the top comment describes. It's explained in more detail on this blog post here.

C code:

const char* getString() {
  return "Hello World!";
}

Compile it with

clang --target=wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -o main.wasm main.c

And JS code:

var importObject = { imports: { imported_func: arg => console.log(arg) } };

WebAssembly.instantiateStreaming(fetch('main.wasm'), importObject)
.then(obj => {

  var charArray = new Int8Array(
    obj.instance.exports.memory.buffer, // WASM's memory
    obj.instance.exports.getString(), // char's pointer
    12                                 // The string's length
  );

  // Convert from ASCII code to char
  let string = String.fromCharCode.apply(null, charArray) 
  console.log(string);
});

Upvotes: 0

GirkovArpa
GirkovArpa

Reputation: 4942

How can I return a JavaScript string from a WebAssembly function?

You cannot. You have to copy it to memory shared with JavaScript.

void hello(char *array) {
    const my_string = "Hello from C++!";
    memcpy(array, my_string, strlen(my_string));
}
  (async () => {
    const response = await fetch('/js/test.wasm');
    const bytes = await response.arrayBuffer();
    const results = await WebAssembly.instantiate(bytes, {});
    const { instance: { exports: { memory, hello } }} = results;
    const array = new Uint8Array(memory.buffer, 0, 15);
    hello(array.byteOffset);
    console.log(new TextDecoder('utf8').decode(array)); // Hello from C++!
  })();

Upvotes: 3

Related Questions