|
| 1 | +# Web Image: Export Java Method Example |
| 2 | + |
| 3 | +This demo illustrates the use of **Web Image** - an experimental backend for [GraalVM Native Image](https://www.graalvm.org/latest/reference-manual/native-image/) that compiles a Java application ahead-of-time and produces a WebAssembly (WASM) module with a JavaScript wrapper. |
| 4 | +Then it can be run in browsers or with Node.js. |
| 5 | + |
| 6 | +The key idea is to show how you can currently **call Java methods directly from JavaScript** without relying on the `main()` method. |
| 7 | +This demo exposes a simple Java `add(int a, int b)` method to the global JavaScript scope using the `@JS` annotation from the [Annotation Interface](https://www.graalvm.org/sdk/javadoc/org/graalvm/webimage/api/JS.html). |
| 8 | + |
| 9 | +> Note: Web Image is an experimental technology and under active development. APIs, tooling, and capabilities may change. |
| 10 | +
|
| 11 | +## Prerequisites |
| 12 | + |
| 13 | +* An [Early Access build](https://github.com/graalvm/oracle-graalvm-ea-builds/releases) of Oracle GraalVM 25 (25e1) or later. |
| 14 | +* All [prerequisites](https://www.graalvm.org/latest/reference-manual/native-image/#prerequisites) required for Native Image building. |
| 15 | +* [Binaryen toolchain](https://github.com/WebAssembly/binaryen) version 119 or later, available on the system path. Web Image uses `wasm-as` from `binaryen` as its assembler. |
| 16 | + * **macOS**: It is recommended to install Binaryen using Homebrew, as the pre-built binaries from GitHub may be quarantined by the operating system: |
| 17 | + ```bash |
| 18 | + brew install binaryen |
| 19 | + ``` |
| 20 | + * **Other platforms**: Download a pre-built release for your platform from [GitHub](https://github.com/WebAssembly/binaryen/releases). |
| 21 | + |
| 22 | +## Building the WebAssembly Module |
| 23 | + |
| 24 | +1. Compile the Java source file: |
| 25 | + ```bash |
| 26 | + javac Adder.java |
| 27 | + ``` |
| 28 | +2. Compile the application to WASM by passing the `--tool:svm-wasm` option (it should be the first argument): |
| 29 | + ```bash |
| 30 | + native-image --tool:svm-wasm -H:-AutoRunVM Adder |
| 31 | + ``` |
| 32 | + The build produces the following artifacts in the working directory: |
| 33 | + - _adder.js_ - a JavaScript runtime wrapper; |
| 34 | + - _adder.js.wasm_- the compiled WebAssembly module containing Java code and runtime elements (object layout, parts of [Substrate VM](https://github.com/oracle/graal/tree/master/substratevm) adapted for Wasm); |
| 35 | + - _adder.js.wat_ - debug artifacts to understand how Java code and runtime components are lowered to WebAssembly. |
| 36 | + |
| 37 | +3. Run the application in a browser using a simple HTTP server (with Python or Java): |
| 38 | + ```bash |
| 39 | + python3 -m http.server 8000 |
| 40 | + ``` |
| 41 | + ```bash |
| 42 | + jwebserver -p 8000 |
| 43 | + ``` |
| 44 | + Instead of just loading the script, you attach some logic after the runtime is ready. |
| 45 | + |
| 46 | +4. Navigate to [http://localhost:8000](http://localhost:8000) in the browser. Enter some numbers, click **Add** and see the result displayed. |
| 47 | + |
| 48 | +## Review the Sample Application |
| 49 | + |
| 50 | +What actually happens? This is the Java source code: |
| 51 | +```java |
| 52 | +import java.util.function.BiFunction; |
| 53 | +import org.graalvm.webimage.api.JS; |
| 54 | +import org.graalvm.webimage.api.JSNumber; |
| 55 | +
|
| 56 | +public class Adder { |
| 57 | + public static int add(int a, int b) { |
| 58 | + return a + b; |
| 59 | + } |
| 60 | +
|
| 61 | + @JS(args = {"adder"}, value = "globalThis.adder = adder;") |
| 62 | + private static native void export(BiFunction<JSNumber, JSNumber, JSNumber> adder); |
| 63 | +
|
| 64 | + public static void main(String[] args) { |
| 65 | + export((a, b) -> { |
| 66 | + return JSNumber.of(add(a.asInt(), b.asInt())); |
| 67 | + }); |
| 68 | + } |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +- `@JS` annotation is part of [GraalVM Web Image API](https://www.graalvm.org/sdk/javadoc/org/graalvm/webimage/api/JS.html). It allows you to bridge Java and JavaScript. |
| 73 | +- `args = {"adder"}` tells GraalVM that the BiFunction you pass in Java will be available as a JavaScript variable `adder`. |
| 74 | +- `value = "globalThis.adder = adder;"` is a raw JavaScript code executed when the export happens; it sets a variable called `adder` to be globally accessible in browsers. |
| 75 | + |
| 76 | +Further down you see the `export` method: |
| 77 | +```java |
| 78 | +export((a, b) -> { |
| 79 | + return JSNumber.of(add(a.asInt(), b.asInt())); |
| 80 | +}); |
| 81 | +``` |
| 82 | + |
| 83 | +- A lambda passed in the `export` method converts JS numbers (`JSNumber`) to Java integers, calls the `add` method, and converts the result back to `JSNumber`. |
| 84 | +When `export` is called, GraalVM runs the `@JS` snippet. |
| 85 | +This makes the lambda directly callable from JS as `globalThis.adder(...)`. |
| 86 | + |
| 87 | +The next part is calling from JavaScript in HTML, which happens in this part of _index.html_: |
| 88 | +```js |
| 89 | +<script> |
| 90 | +GraalVM.run([], {}).then(() => { |
| 91 | + ... |
| 92 | + addButton.addEventListener("click", () => { |
| 93 | + const a = parseInt(document.getElementById("num1").value); |
| 94 | + const b = parseInt(document.getElementById("num2").value); |
| 95 | +
|
| 96 | + // Call the Java add function via WebAssembly |
| 97 | + const result = globalThis.adder(a, b); |
| 98 | +
|
| 99 | + output.innerText = `Result: ${result}`; |
| 100 | + }); |
| 101 | + ... |
| 102 | +}); |
| 103 | +</script> |
| 104 | +``` |
| 105 | +
|
| 106 | +- `GraalVM.run([], {})` initializes the WASM module and the Java runtime inside the browser. |
| 107 | +- `globalThis.adder(a, b)` calls the `add` function you exported via WebAssembly. |
| 108 | +
|
| 109 | +### Conclusion |
| 110 | +
|
| 111 | +The focus of this demo is to demonstrate direct interaction between Java and JavaScript in the browser via WebAssembly. |
| 112 | +Note that the [GraalVM Web Image API](https://www.graalvm.org/sdk/javadoc/org/graalvm/webimage/api/JS.html) is still under active development. |
0 commit comments