konsoletyper / teavm

Compiles Java bytecode to JavaScript, WebAssembly and C
https://teavm.org
Apache License 2.0
2.55k stars 260 forks source link

[WASM] Missing exported functions on WASM instance #924

Closed TrOllOchamO closed 1 month ago

TrOllOchamO commented 1 month ago

Hello, I've been refactoring my project and everything looked fine until I realized that java functions marked with @Export(name = "exportedName") were not attached to the teavm.instance.exports as before.

Since I haven't done anything too suspicious in the code, I suspect it might be due to my modification in the pom.xml. So I was wondering what part of the maven TeaVM dependencies are responsible for the export of the java functions and have I suppressed it by mistake ?

Here is my pom.xml if you see anything obviously missing ```xml 4.0.0 flexio wasm-service 1.0-SNAPSHOT wasm-service io.flexio flexio-service-parent 1.341.0-SNAPSHOT UTF-8 1.8 1.8 4.0.1 0.10.0 3.2.0 flexio-snapshots flexio-snapshots https://mvn.ci.flexio.io/flexio-snapshots/ false true flexio-releases flexio-releases https://mvn.ci.flexio.io/flexio-releases/ true false junit junit test org.teavm teavm-interop ${teavm.version} provided io.flexio.apis.ui.standard.entity.ui flexio-standard-entity-ui-api-types com.github.cliftonlabs json-simple ${json.simple.version} org.teavm teavm-classlib ${teavm.version} provided org.teavm teavm-maven-plugin ${teavm.version} web-client compile ${project.build.directory}/generated/ flexio.Main true true true false FULL WEBASSEMBLY org.apache.maven.plugins maven-dependency-plugin check-dependencies analyze-only true true org.teavm org.apache.maven.plugins maven-resources-plugin ${maven.resources.plugin.version} copy-webapp-resources package copy-resources ${project.build.directory}/site src/main/webapp ${project.build.directory}/generated *.wasm *.js ```

If not, I can provide a repo for you to reproduce :+1: Thanks in advance for your help, --Barnabé

TrOllOchamO commented 1 month ago

Ok, after way too much time I manage to understand the problem, It wasn't caused by a missing pom.xml dependence, but rather how exports works.

I had something like this :

// Main.java
package mypackage;

public class Main {
    public static void main(String[] args) {
    }
}
// Foo.java
package mypackage;

import org.teavm.interop.Export;

public class Foo {
    @Export(name = "bar")
    private static void bar() {
        // does something here
    }
    // ...
}

And when I was calling teavm.instance.exports.bar() on the JS side, I had an error because the exports object did not have any bar function attached. The reason was that I don't use my class Foo anywhere in my java code, so the class was discarded all together, even if it has an exported method attached to it. I bodged this by adding a dummy method to the Foo class and calling it in the main, so the Foo class won't be considered unused anymore.

Like so ```java // Foo.java public class Foo { public satic void init() {} // New // ... } ``` ```java // Main.java public class Main { public static void main(String[] args) { Foo.init(); // ensure TeaVM properly export the bar function } } ```

It works, but it feels like a dirty trick, so I was wondering :

Thanks, --Barnabé

konsoletyper commented 1 month ago

Another way to fix the issue is to place all of your exported methods in main class.

Is discarding unused methods an intended behavior even when they are marked with @Export ? I feel like functions marked with @Export should always be exported because we are not able to know in advance if the function will be called or not.

I don't share your feeling. The current behaviour is totally ok.

Is there a better way to fix my code ? Is there a way to mark a function to always be exported that I missed

You can use preservedClasses setting in your build configuration.