emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.67k stars 3.29k forks source link

Binding issues with Djinni #20164

Open SamKouteili opened 1 year ago

SamKouteili commented 1 year ago

I'm attempting to generate a very simple MathUtils class with one function: add_fff64, from c++ to JavaScript.

#include "MathUtils.hpp"

class MathUtils_ : public MathUtils {
public:
    double add_fff64(double a, double b) override {
        return a + b;
    }
};

Using Snapchat/Djinni's wasm interface, I've generated relevant binding files:

// ***wasm/MathUtils.hpp***
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from MathUtils.djinni

#pragma once

#include "src/MathUtils.hpp"
#include "snap.djinni/support-lib/wasm/djinni_wasm.hpp"

namespace djinni_generated {

struct MathUtils : ::djinni::JsInterface<::MathUtils, MathUtils> {
    using CppType = std::shared_ptr<::MathUtils>;
    using CppOptType = std::shared_ptr<::MathUtils>;
    using JsType = em::val;
    using Boxed = MathUtils;

    static CppType toCpp(JsType j) { return _fromJs(j); }
    static JsType fromCppOpt(const CppOptType& c) { return {_toJs(c)}; }
    static JsType fromCpp(const CppType& c) {
        ::djinni::checkForNull(c.get(), "MathUtils::fromCpp");
        return fromCppOpt(c);
    }

    static em::val cppProxyMethods();

    static double add_fff64(const CppType& self, double w_a,double w_b);

};

} // namespace djinni_generated
// ***wasm/MathUtils.cpp***
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from MathUtils.djinni

#include "wasm/MathUtils.hpp"  // my header

namespace djinni_generated {

em::val MathUtils::cppProxyMethods() {
    static const em::val methods = em::val::array(std::vector<std::string> {
        "addFff64",
    });
    return methods;
}

double MathUtils::add_fff64(const CppType& self, double w_a,double w_b) {
    try {
        auto r = self->add_fff64(::djinni::F64::toCpp(w_a),
                  ::djinni::F64::toCpp(w_b));
        return ::djinni::F64::fromCpp(r);
    }
    catch(const std::exception& e) {
        return ::djinni::ExceptionHandlingTraits<::djinni::F64>::handleNativeException(e);
    }
}

EMSCRIPTEN_BINDINGS(_MathUtils) {
    em::class_<::MathUtils>("MathUtils")
        .smart_ptr<std::shared_ptr<::MathUtils>>("MathUtils")
        .function("nativeDestroy", &MathUtils::nativeDestroy)
        .function("addFff64", MathUtils::add_fff64)
        ;
}

} // namespace djinni_generated

While this has indeed compiled into a JavaScript output, there is no semblance of the MathUtils class or any add_fff64 function in MathUtils.js. Indeed, when I attempt to call the add_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 and wasm2wat, MathUtils class and addFff64 both appear, which is why I suspect this may be an issue with emscripten

...
 - 0010490: 7562 6c65 3e00 6164 6446 6666 3634 0048  uble>.addFff64.H
 ...

Is there any way of confirming what may be the root cause of the issue?

Version of emscripten/emsdk: emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.45-git

sbc100 commented 1 year ago

I suspect that this is an issue that is better responded to over in the https://github.com/Snapchat/djinni repo.

It no suprise that MathUtils doesn't show up in the generated JS file though.. all that code would be in the .wasm.

What is the error you see when you try to call that function? Where are you trying to call it from?

SamKouteili commented 1 year ago

I'm currently calling it from the following index.html file:

<!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>

And get the following error:

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)

Will be sure to also ping this to the folks at Djinni!