Skip to content

Compiler Errors and Problems

Volker Berlin edited this page Jun 26, 2022 · 5 revisions

Error: Native methods cannot be compiled to WebAssembly: java/lang/Foo.bar()V

JWebAssembly can only compile pure Java code into WebAssembly code. The JDK contains a lot of native code. If you want to use this native Java code, you need a library that provides a replacement for the native methods. For the browser, you can use the jwebassembly-api library. For a WAPI target, you need a different library.

  1. if you have not added a library, the first step is to add one.
  2. if this does not help, then you can write a replacement for the method using the @Replace annotation.
  3. alternatively, you can analyze why this native method is needed. If you set the debug flag, the compiler will output in what order the methods were scanned. If there is a method in the scan order that you never need at runtime, then override it to stop the scan and compilation at that point.

If you find a good replacement for a native method that can also help others, make a pull request [jwebassembly-api] (https://github.com/i-net-software/JWebAssembly-API).

Problem: Very small output file

If the WebAssembly output file (*.wasm) is very small (about 100 bytes), then the compiler did not find an @Export annotation. The compiler compiles only the methods marked with the @Export annotation and all the referenced Java code. The cause may be:

  1. adding the Java sources failed. Are there compiled Java classes?
  2. you have not added the @Export annotation to one of your methods.

Problem: Understanding the debug log level output of the compiler

JWebAssembly is not a JIT like Java. It is an AOT compiler. But the compiler does not compile all Java Code. It if follow only the references starting with the @Export annotation. If you enable the debug log level then you can see which Java Code was compiles because of which references. The debug log level is is not to be confused with the debugNames flag. This can be helpful to find an option to produce a smaller output or to prevent the usage of some native code. The compiler does not know which code branches will never need at runtime. It is follow all references. It is the job of a human intelligence to find such point of optional branches that never is used.

For example can an x instanceof File add an reference to the complete file stack and it native methods. If you override this code place or you override the completely File class with an stub this can stop the compiler to follow the references.

If you follow back the scan hierarchy then find the Java code that reference the method. This can you make recursively to find a good place to break the references. A snippet output can look like:

scan java/lang/Number.<init>()V
	type: java/lang/Number
	call: java/lang/Object.<init>()V
...
scan java/lang/Object.<init>()V

We see that the constructor of java.lang.Number was scanned. The constructor has a reference to the type java.lang.Number because this method has a parameter of this type. And there is a call to the constructor of java.lang.Object. Because of this reference the compiler will also scan and compile the constructor of java.lang.Object.