0

How can I wrap the printf() function with console.log? I want to write a small webassembly demo but I can't interprete the following js console output. I compiled the C code with Clang to wasm file.

C Code:

#include <stdio.h>

int main(void)
{
    printf ("test");
    return 0;
}

JS Code:

  const config = {
    env: {
        memory_base: 0,
        table_base: 0,
        memory: new WebAssembly.Memory({
            initial: 256,
        }),
        table: new WebAssembly.Table({
            initial: 0,
            element: 'anyfunc',
        }),
        printf: msg => console.log(msg),
    }
}

fetch('test.wasm')
    .then(response =>
        response.arrayBuffer()         
    )
    .then(bytes => {
        return WebAssembly.instantiate(bytes, config); 
    })
    .then(results => { 
       var { main } =  results.instance.exports;
       main();
    });

The console output is always: "1024" , independent of the printf() input parameter. But I want to get the string "test" as output. How to interprete this result? Is it a pointer to a memory address? When I use printf('a') I get 97 as console output, the ascii representation of the char.

Solution:

let buffer;

const config = {
    env: {
        memory_base: 0,
        table_base: 0,
        memory : new WebAssembly.Memory({ initial: 256}),
        table: new WebAssembly.Table({
            initial: 0,
            element: 'anyfunc',
        }),
        printf: index=> {
            let s = "";
            while(true){
                if(buffer[index] !== 0){
                    s += String.fromCharCode(buffer[index]);
                    index++;
                }else{
                    console.log(s);
                    return;
                }
            }
        }
    }
};

fetch(url)
    .then(response =>{
        return response.arrayBuffer();
    })
    .then(bytes => {
        return WebAssembly.instantiate(bytes, config); 
    })
    .then(results => { 
       let { main } =  results.instance.exports;
       buffer = new Uint8Array(results.instance.exports.memory.buffer);
       main();
    });
Steve D
  • 38
  • 1
  • 9
  • Why aren't you just use Emscripten? – Bumsik Kim Aug 01 '19 at 13:05
  • I know Emscripten has cwrap/ccall functions but I have to use Clang compiler. It's a customer specification :) – Steve D Aug 01 '19 at 13:36
  • Emscripten uses clang/LLVM as backend though. My advice is that you will want to use Emscripten at some point, otherwise you have to re-implement every C libraries and runtime... – Bumsik Kim Aug 01 '19 at 13:58
  • Anyway, could you post your clang command? Including clang compile flags. – Bumsik Kim Aug 01 '19 at 13:59
  • Ok I try to change the compiler to Emscripten, but at the moment I just use a predefined docker environment with clang inside. The command inside the container is "clang --target=wasm32 -Wl,--initial-memory=131072,--allow-undefined,--export=main,--no-threads,--strip-all,--no-entry -O2 -s -o test.wasm test.c" Everything is correctly compiling and running, I am just try to do some experiments with c/js function wrapping. – Steve D Aug 01 '19 at 14:49

1 Answers1

0

WebAssembly doesn't have direct support for string types - therefore, when the printf("test") statement is compiled, the "test" literal is converted into a reference to an address in linear memory that holds the actual string.

You'll have to work out how to perform the dereferencing / encode / decode yourself. See the following for more details:

How can I return a JavaScript string from a WebAssembly function

ColinE
  • 68,894
  • 15
  • 164
  • 232