Closed SamKouteili closed 10 months ago
How do you call the function from JS in your html file? Did you load the module and access the function through the module object? Here's an example of calling wasm module from JS https://github.com/Snapchat/djinni/blob/main/test-suite/handwritten-src/js/test.html#L40
I attempt to call it in a simple HTML file index.html
in the same directory as the js
and wasm
output:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MyClass Test</title>
</head>
<body>
<script src="MathUtils.js"></script>
<script>
Module.onRuntimeInitialized = function() {
var instance = new Module.MathUtils();
console.log("successfully instantiated");
console.log(Module.MathUtils.addFff64(10, 8));
instance.delete();
};
</script>
</body>
</html>
I get the following error on my browser:
MathUtils.js:2182 Uncaught (in promise) BindingError: MathUtils has no accessible constructor
at ClassHandle.<anonymous> (MathUtils.js:2182:21)
at new MathUtils (MathUtils.js:1714:23)
at Module.onRuntimeInitialized (index.html:11:28)
at doRun (MathUtils.js:3963:71)
at run (MathUtils.js:3980:5)
at runCaller (MathUtils.js:3924:19)
at removeRunDependency (MathUtils.js:623:7)
at receiveInstance (MathUtils.js:842:5)
at receiveInstantiationResult (MathUtils.js:860:5)
Should I be adding a constructor to my djinni interface? That also hasn't seemed to work. I've tried changing the script in the html file to:
Module().then(module => {
console.log(module);
// runTests(module, allTests);
})
But get the error "module is not a function".
As an aside, Ive also tried making it so that add_fff64
is a static function, and ammending MathUtils.cpp
to the following:
// src/MathUtils.cpp
#include "MathUtils.hpp"
double MathUtils::add_fff64(double a, double b) {
return a + b;
}
However, I get a compilation error as an add_fff64
function is defined in both src/MathUtils.cpp
and wasm/MathUtils.cpp
. How are we expected to define static functions when exporting to js?
I think you probably missed the "-s MODULARIZE=1" flag in your emcc command line. See the flags we use for compiling wasm code here https://github.com/Snapchat/djinni/blob/master/test-suite/BUILD#L148
I just added the flag, and indeed now no longer face any error message, but also have nothing printed to the console, so this seems a little difficult to debug. When I instead changed my script to:
Module().then(module => {
console.log(module);
})
There is no semblance of an addFff64
method. Indeed, (typeof module.addFff64 === 'function')
returns a false.
I've added my repository to the following repo: https://github.com/SamKouteili/djinni2emcc for reproduction. Hoping to write a blog post detailing how to go about doing this so future devs do not face the same issues :)
Do you have module.MathUtils.addFff64
?
See the generated binding here:
EMSCRIPTEN_BINDINGS(MathUtils) {
class_<MathUtils>("MathUtils")
.constructor<int>()
.class_function("addFff64", &MathUtils::add_fff64)
;
}
Your function is inside MathUtils
Unfortunately, module.MathUtils.addFff64
is also not defined. When I run the following:
console.log(typeof module);
console.log(typeof module.MathUtils);
console.log(module.MathUtils.addFff64);
I get
object
function
[undefined]
I assumed this may be because addFff64 is not a static method (this is the final intended behaviour, but I still am not sure how to get around the compiler issue of having add_fff64 defined in both src/MathUtils.cpp and the generated wasm/MathUtils.cpp). As such, I tried instantiating an instance of MathUtils:
console.log(typeof module);
console.log(typeof module.MathUtils);
console.log(module.MathUtils.addFff64);
const mathUtilsInstance = new module.MathUtils();
console.log(mathUtilsInstance);
console.log(mathUtilsInstance.addFff64(1, 2));
But received an error that there is no valid constructor for the MathUtils module.
Your function is not a static one:
MathUtils = interface +c {
add_fff64(a: f64, b: f64): f64; # this is an instance method
}
If you want to directly call the function without an object instance, you need to define the method as static:
MathUtils = interface +c {
static add_fff64(a: f64, b: f64): f64;
}
and implement the static method in C++:
double MathUtils::add_fff64(double a, double b) {
return a + b;
}
If you want to use object instances, you would do something like this:
MathUtils = interface +c {
add_fff64(a: f64, b: f64): f64;
static create_math_utils(): MathUtils;
}
Then implement the static factory method in C++:
std::shared_ptr<MathUtils> MathUtils::create_math_utils() {
return std::make_shared<MathUtils>();
}
Fantastic, these were exactly the issues I was facing. I was able to generate both a static version where add_fff64 is a static function that can be called without instantiation, as well as a version where MathUtils needed to be initialized. Thank you truly for all the help and patience throughout! Will be publishing findings in some capacity so that other developers learn from the mishaps I've faced.
I'm attempting to generate a very simple
MathUtils
class with one function:add_fff64
, from c++ to JavaScript.Using Snapchat/Djinni's wasm interface, I've generated relevant binding files, and have also generated the cpp header:
While this has indeed compiled into a JavaScript output, there is no semblance of the
MathUtils
class or anyadd_fff64
function inMathUtils.js
. Indeed, when I attempt to call theadd_fff64
function in a generic native html file, I get an error. I use the following script:emcc wasm/MathUtils.cpp src/MathUtils.cpp /Users/sam/snap.djinni/support-lib/wasm/djinni_wasm.cpp -o MathUtils.js --bind
This is particularly strange to me because when debugging the generated WebAssembly file with tools such as
wasm-objdump
andwasm2wat
,MathUtils
class andaddFff64
both appear.Is there any way of confirming what may be the root cause of the issue?