Search code examples
javascriptnode.jscwebassemblyemscripten

Doing the Emscripten Tutorials - Why does int_sqrt(25) not return 5 in this code?


What are the details of your problem?

So far using Emscripten, I've converted this C code to a.out.wasm and a.out.js:

#include <math.h>
#include <emscripten.h>

int int_sqrt(int x) {
  return sqrt(x);
}

I require'd the output and I'm trying to test out using the int_sqrt() function in Node by using cwrap() in this code:

a = require('./a.out');

int_sqrt = a.onRuntimeInitialized = () => {
    return a.cwrap('int_sqrt', 'number', ['number']);

};
console.log(int_sqrt(25));

The output I'm getting is [Function (anonymous)] even though I think I'm passing in 25 to the "cwrapped" int_sqrt function.

My question is: How can I get this to let me use the int_sqrt() function for the rest of my code and have it give the correct output without putting all my code inside the initializing function?


What did you try and what were you expecting?

I'm new to programming and I'm still trying to learn concepts so I would really appreciate it if someone could explain it to me. I think it might have to do with scope? But I'm not sure.

Following the code exactly off of the tutorials:

int_sqrt = Module.cwrap('int_sqrt', 'number', ['number'])
int_sqrt(12)
int_sqrt(28)

would give me an error of Aborted(Assertion failed: native function called before runtime initialization) Error. So I edited it to use Module.onRuntimeInitialized on someone else's recommendation from here.

Using int_sqrt() inside of the scope like this seems to work and returns 5.

a = require('./a.out');

a.onRuntimeInitialized = () => {
     int_sqrt = a.cwrap('int_sqrt', 'number', ['number']);
     console.log(int_sqrt(25));
};

But it doesn't let the rest of my code use int_sqrt() properly so I'm wondering how can I get it to.

Thank you if someone could explain it.


Solution

  • I don't see anything in the documentation for onRuntimeInitialized saying that it returns what the callback it calls returns. From what you've described, apparently it doesn't.

    Using int_sqrt() inside of the scope like this seems to work and returns 5...But it doesn't let the rest of my code use int_sqrt() properly so I'm wondering how can I get it to.

    Since you have to wait for the runtime to be initialized, you'll have to wait for that callback.

    Assuming Emscripten works with JavaScript's native modules ("type": "module" in package.json, details), you could switch to those, wrap the callback in a promise, and use await at the top level to wait for the promise to settle.

    Here's a sketch:

    import a from "./a.out'";
    
    const { int_sqrt } = await new Promise((resolve) => {
        a.onRuntimeInitialized = () => {
            try {
                const int_sqrt = a.cwrap("int_sqrt", "number", ["number"]);
                resolve({ int_sqrt });
            } catch (error) {
                reject(error);
            }
        };
    });
    
    // ...use `int_sqrt` here...
    

    If you need to do other functions, just add them to that template:

    import a from "./a.out'";
    
    const { int_sqrt, int_mult } = await new Promise((resolve, reject) => {
        a.onRuntimeInitialized = () => {
            try {
                const int_sqrt = a.cwrap("int_sqrt", "number", ["number"]);
                const int_mult = a.cwrap("int_mult", "number", ["number"]);
                resolve({ int_sqrt, int_mult });
            } catch (error) {
                reject(error);
            }
        };
    });
    
    // ...use `int_sqrt` and `int_mult` here...
    

    You could make that a module that exports the functions, which are then used by other modules; for instance, at the end of the above:

    export { int_sqrt, int_mult };
    

    Then in another module:

    import { int_sqrt, int_mult } from "./the-module-above.js";