Skip to content

Yann39/java-wasm-teavm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java WASM TeaVM

Exploring WebAssembly and testing in Java through TeaVM.

Version Static Badge

Version Version


Table of Contents

About the Project

Java logo WebAssembly logo Maven logo

This is just to explore a bit about WebAssembly and specially in Java.

What is WebAssembly ?

  • W3C standard
  • Presented in June 2015, released in March 2017, W3C recommendation since December 2019
  • Binary instruction format (bytecode)
  • Executable in a virtual machine (wasm runtime)
  • Portable (not architecture or microprocessor dependent), x86 / ARM / RISC-V compatible
  • Secure (runs in a sandbox, separate from the host runtime)
  • Compilable from several languages (currently 40+ languages), C, C++, Rust, Go, and more recently Java, Kotlin, C#, Dart, Swift, Zig, ... and even interpreted languages such as PHP, Python or Ruby via interpreter compilation
  • Executable in a browser (compatible with Firefox, Chrome, Safari, Opera, Edge since 2017) via Web APIs
  • Executable on a server or in a container via WASI / WasmEdge
  • Lightweight (small binaries), and performance close to native (> JavaScript)

Promise

  • Create binaries for the web from any language, executable anywhere
  • Create libraries that can be used across multiple languages
  • Create web applications that can use several languages on the same page

WASM vs Docker

Quote from Solomon Hykes (co-founder of Docker) :

If WASM+WASI existed in 2008, we wouldn't have needed to create Docker. That's how important it is. WebAssembly on the server is the future of computing

WASM and Docker can be integrated via Docker WASM (beta) and/or container2wasm.

Linux/Windows containers depend on the processor architecture and the operating system (on Linux, you can only run Linux containers, and on Windows only Windows containers (or Linux, but via a VM through Hyper-V)), this is not the case with WASM containers.

Compilers

Programs must be compiled to obtain WebAssembly modules. For interpreted languages, the interpreter is compiled into WebAssembly.

  • For C / C++ (every language that uses LLVM) : emscripten

    sudo apt install emscripten
    emcc hello.c -o hello.html
  • For Rust : wasm-pack

    cargo install wasm-pack
    wasm-pack build --target web
  • For Flutter (Dart) : via js_interop package and flag --wasm (on browsers that support WasmGC)

    flutter build web --wasm
  • For Go : Native since Go 1.11 via environment variables

    GOOS=js GOARCH=wasm go build -o main.wasm
  • For Java : JVM bytecode to WebAssembly via Bytecoder, TeaVM or Cheerpj

    java -jar bytecoder-cli.jar -classpath=. -mainclass=Hello -builddirectory=. -backend=wasm 

Docker & Wasm

Wasm workloads

  • Currently, you need to activate the feature in Docker Desktop

  • Then specify the runtime and platform at runtime :

    docker run --runtime=io.containerd.wasmedge.v1 --platform=wasi/wasm mywasm

Image to WASM via container2wasm :

Converting image to WASM, i.e. :

c2w ubuntu:22.04 /tmp/wasm/out.wasm

Running in a browser through Apache, i.e. :

c2w ubuntu:22.04 /tmp/out-js2/htdocs/out.wasm
cp -R ./examples/wasi-browser/* /tmp/out-js2/ && chmod 755 /tmp/out-js2/htdocs
docker run --rm -p 8080:80 \
         -v "/tmp/out-js2/htdocs:/usr/local/apache2/htdocs/:ro" \
         -v "/tmp/out-js2/xterm-pty.conf:/usr/local/apache2/conf/extra/xterm-pty.conf:ro" \
         --entrypoint=/bin/sh httpd -c 'echo "Include conf/extra/xterm-pty.conf" >> /usr/local/apache2/conf/httpd.conf && httpd-foreground'

Performance

Usage

Statistics 2023

Also used in various blockchains (Cosmos, Polkadot, MultiversX, Near Protocol) !

Examples

Some WASM applications that run in the browser :

List of projects made with WebAssembly : https://madewithwebassembly.com/

Conclusion

  • Despite the promise of speed, the tools I've tried are pretty slow
  • Most features are experimental, almost exclusively in preview or alpha versions
  • The standard is incomplete and continues to evolve (rapidly though)
  • Difficult to debug and profile (lack of tools)

Potential uses :

  • Training, easily give users a ready-to-use environment (i.e. WordPress, Linux, Python, etc.)
  • Testing new architectures, new distributions, etc.
  • Libraries (shared across different systems / languages), even if TeaVM is not designed for this currently

Java - TeaVM

TeamVM is a Java bytecode compiler that emits JavaScript and/or WebAssembly to be executed in the browser.

  • It does not require source code, only compiled files (not like GWT, for example)
  • it also supports Kotlin and Scala

Minimal example

Here is a minimal example to compile a Java function into WebAssembly and then use it in JavaScript within an HTML file.

  1. Create a new Maven project. Here's a basic pom.xml configuration that includes the teavm-maven-plugin to compile Java to WebAssembly :

    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.example</groupId>
        <artifactId>teavm-wasm-example</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.teavm</groupId>
                <artifactId>teavm-classlib</artifactId>
                <version>${teavm.version}</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.teavm</groupId>
                    <artifactId>teavm-maven-plugin</artifactId>
                    <version>0.9.2</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                            <configuration>
                                <mainClass>com.example.HelloWasm</mainClass>
                                <targetDirectory>${project.basedir}/src/main/webapp/wasm</targetDirectory>
                                <targetFileName>helloworld</targetFileName>
                                <targetType>WEBASSEMBLY</targetType>
                                <optimizationLevel>FULL</optimizationLevel>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
  2. Create a minimal Java class with a simple static method that you want to compile to WebAssembly and export it :

    package com.example;
    
    import org.teavm.interop.Export;
    
    public class HelloWasm {
    
        public static void main(String[] args) {}
    
        @Export(name = "add")
        public static int add(int a, int b) {
            return a + b;
        }
    
    }

    As stated in the docs, TeaVM is not designed to port libraries, so we should always have a "main" method, to serve as an entry point.

  3. Create the HTML and JavaScript to Use WASM :

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>TeaVM WASM Example</title>
        <script src="wasm/helloworld.wasm-runtime.js"></script>
    </head>
    <body>
        <h1>TeaVM WASM Example</h1>
        <p id="output"></p>
        <script>
            async function loadWasm() {
                const teavmInstance = await TeaVM.wasm.load('wasm/sumwasm.wasm');
                const result = teavmInstance.instance.exports.add(10, 20);
                document.getElementById('output').textContent = 'Result : ' + result;
            }
            loadWasm();
        </script>
    </body>
    </html>
  4. Build the project :

    mvn clean package

    After building, the WebAssembly file helloworld.wasm should be located in src/main/webapp/wasm/.

    It should also have generated a helloworld.wasm-runtime.js file, designed to handle the instantiation and management of the WebAssembly module. The script sets up necessary functions and imports, making it easier to work with the WebAssembly module (it is loaded in our HTML file).

  5. Run the HTML file :

    Make sure to copy the HTML file in the proper location (above the wasm directory containing the .wasm and .js files) then just run it with your preferred browser, it should display Result : 30.

Project's examples

This project contains 3 examples :

  • HelloJs : compiled to Javascript, to demonstrate how to generate a JavaScript file from Java, that can be used from HTML
  • SumWasm : compiled to WASM, to demonstrate how to export a Java function to WebAssembly module and call it from Javascript
  • HelloWasm : compiled to WASM, to demonstrate how to export a Java function to WebAssembly module and call it from Javascript, and also how to import a javascript function that can be redefined in JavaScript (2-way interaction)

Note

The HelloWasm example does not work as expected (will display "undefined" instead of the hello world string), this is because WebAssembly itself does not natively support string types (or any other complex data structures), so managing strings between Java, WebAssembly and JavaScript involves some additional steps. I think it's a bit of a tinkering, so I haven't gone any further for the moment, I prefer to wait for improvements to the framework so that this can be managed more easily.

Configuration is done via the Maven plugin teavm-maven-plugin, see TeaVM.

The plugin defines an execution for each of the example, you can at the configuration in the pom.xml.

Basically, for WASM examples, TeaVM compiles the Java class into a WASM binary, the JavaScript loads this WASM file, instantiate it, and call the exported methods. The result is displayed on the webpage.

TeaVM automatically creates corresponding JS files containing the necessary JavaScript functions that the WebAssembly module requires, it also contains helpers to handle the instantiation and management of the WebAssembly module, this is why we load the corresponding JavaScript runtime file in each HTML file.

In case you don't want to generate / include these files, you will need to define the imports manually, for example :

// define the imports required by TeaVM, including the logInt, logString, and currentTimeMillis functions
const imports = {
    teavm: {
        // provide the current time in milliseconds
        currentTimeMillis: () => Date.now(),

        // log strings passed from WASM to the browser's console
        logString: (address) => {
            const memory = instance.exports.memory;
            const bytes = new Uint8Array(memory.buffer, address);
            let str = '';
            for (let i = 0; i < bytes.length && bytes[i] !== 0; i++) {
                str += String.fromCharCode(bytes[i]);
            }
            console.log(str);
        },

        // log integers passed from WASM to the browser's console
        logInt: (value) => {
            console.log(value);
        }
        
        ...
    }
};

// instantiate the WebAssembly module with the imports
const result = await WebAssembly.instantiate(bytes, imports);

Usage

Simply package the project to generate the WASM and JS binaries :

mvn clean package

The TeaVM Maven plugin will generate the files in the target/webapp directory.

Then copy the HTML files into the target/webapp/ folder and launch them in your preferred browser.

License

General Public License (GPL) v3

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

About

Some Java to WebAssembly examples using TeaVM

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published