What is Webassembly
A lot of things have changed since the first web page was published. Back in the days it was a static text file with some html markup. For the last so few decades web pages evolved to some really thick clients allowing a lot of interaction and having a lot of functionality running in the browser on the client side. There was only one issue in this evolution – the only way to develop this functionality was using JavaScript. This scripting language, designed exactly 24 years ago, was not designed to be used to build such heavy or involved functionalities. As a result, today’s web faces a lot of performance issues and is really bound to one and only technology. Modern browsers sometimes do real magic to speed up the performance of web pages, but it is obvious, We should not try to work around the technology limitations by throwing more hardware, compute and memory resources at it. Every time we load a web page, we load HTML, CSS and JS data, we parse it, create AST, optimize it, compile or interpret it. It is a huge waste of resources.
Since it is really hard to break de-facto standards, W3C has created an extension technology called WebAssembly. We should be clear here – this is not a full replacement of JavaScript, but an extension, focused on high performance.
So, according to Wikipedia, WebAssembly (often shortened to Wasm) is an open standard that defines a portable binary code format for executable programs, and a corresponding textual assembly language, as well as interfaces for facilitating interactions between such programs and their host environment. The main goal of WebAssembly is to enable high performance applications on web pages, but the format is designed to be executed and integrated in other environments as well.
Wasm code (binary or bytecode) is intended to be run on a portable virtual stack machine (VM). The VM is designed to be fast, to parse and execute than JavaScript and to have a compact code representation. A Wasm program is designed to be a separate module containing collection of various wasm-defined value and program types definitions expressed either in binary or textual format (that both have a common structure).
Except in a virtual machine, wasm can also be compiled to native binary (with 3rd party compilers), and this is an easy way to release something that is not usually native as native, as long as it doesn’t require heavy runtime libraries.
WebAssembly is a fairly new technology and its development is in progress. But there already is an MVP (Minimal Viable Product) implemented in the most of the browsers and available out of the box. But, to make it really worldwide standard, we need some more time!
There have been around 40 programming languages reported to support Wasm as a compilation target. Java and Kotlin are among them!
https://hacks.mozilla.org/2017/02/a-crash-course-in-assembly/
Various tutorials are available in YouTube and other platforms for better understanding WebAssembly. It is strongly recommended to get more acquainted with WebAssembly before reading this article.
What about Java?
Let us try to create a WebAssembly from Java code. For this we have several options.
One of them is JWebAssebmly, which is is a Java bytecode to WebAssembly compiler. It uses Java class files as input. That it can compile any language that compile to Java bytecode like Clojure, Groovy, JRuby, Jython, Kotlin and Scala. As output it generates the binary format (.wasm file) or the text format (.wat file). The target is to run Java natively in the browser with WebAssembly.
To export a Java function to make it accessible from JavaScript, you must add the annotation de.inetsoftware.jwebassembly.api.annotation.Export.
1 2 3 4 5 6 |
import de.inetsoftware.jwebassembly.api.annotation.Export; @Export public static int add( int a, int b ) { return a + b; } |
As a result the following wasm will be generated (text representation):
1 2 3 4 5 6 |
(module (export "add" (func $local/samples/mitia/Add.add)) (func $local/samples/mitia/Add.add (param i32) (param i32) (result i32) get_local 0 get_local 1 i32.add return )) |
To import a JavaScript function to make it accessible from Java, you must add the annotation de.inetsoftware.jwebassembly.api.annotation.Import. The method can be declared native or can have a Java implementation which will be ignored on compiling.
1 2 3 4 5 |
import de.inetsoftware.jwebassembly.api.annotation.Import; @Import( module = "global.Math", name = "max" ) static int max( int a, int b) { return Math.max( a, b ); } |
The project is very young. It is only version 0.1 so there are a lot of limitations.
Another option will be TeaVM.
We can set it up as s regular maven/gradle dependency:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
... <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-classlib</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-jso-apis</artifactId> <version>${project.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-interop</artifactId> <version>${project.version}</version> </dependency> ... |
Write some Java code of the stuff we want to export as a Wasm binary. @Export annotation tell us that this function will be visible from JavaScript
1 2 3 4 5 6 |
public class WasmFunctions { @Export(name = "thePurposeOfLife") public static int getThePurposeOfLife(){ return 43; } } |
With a maven plugin we specify where to export the generated WebAssemblies (take a look at the TargetType):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<plugin> <artifactId>maven-war-plugin</artifactId> <version>2.4</version> <configuration> <webResources> <resource> <directory>${project.build.directory}/generated/wasm</directory> </resource> </webResources> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.teavm</groupId> <artifactId>teavm-maven-plugin</artifactId> <version>${project.version}</version> <executions> <execution> <id>wasm-client</id> <goals> <goal>compile</goal> </goals> <configuration> <targetDirectory>${project.build.directory}/generated/wasm/wasm</targetDirectory> <mainClass>local.mitia.wasm.WasmFunctions</mainClass> <debugInformationGenerated>true</debugInformationGenerated> <targetType>WEBASSEMBLY</targetType> <optimizationLevel>FULL</optimizationLevel> <heapSize>8</heapSize> </configuration> </execution> </executions> </plugin> |
Then just prepare the html file:
1 2 3 4 5 6 7 8 9 |
<html> <head> <meta charset='UTF-8'> <script src="output.js"></script> </head> <body> <label id='wasm'></label> </body> </html> |
And the JS file:
1 2 3 4 5 6 7 8 |
if ('WebAssembly' in window) { // Set the import object in instantiateStreamingWebAssembly.instantiateStreaming(fetch('wasm/classes.wasm')).then(result => { //the Purpse of Life is: result = result.instance.exports.thePurposeOfLife(); document.getElementById('wasm').innerHTML ='The Purpose of life according to teavm wasm from java: ' + result;}); } |
And we are done! Now we can code in Java, compile it to wasm, and run it on the client side!
You can play with this code here https://github.com/dalexandrov/WebAssemblyFromJava
How about Kotlin?
They did it their own way. Since Kotlin has an experimental project Kotlin/Native, which has the compilation target of LLVM, it serves as a base to generate wasm binary out of it.
We should only specify a build target of “wasm32” in our gradle build script and as a result, on the output, we should get wasm binaries and a JavaScript wrapper to work with this assembly.
This project is still experimental and there are a lot of thing to be done. Unfortunately, the documentation is also “in progress”, so you will need a little hacking to play with it. But it is a lot of fun!
Can we use Webassembly from Java?
On the other hand, Java can also “consume” WebAssembly, or to be more precise GraalVM can do this.
In his article Aleksander Prokopec has announced that GraalWasm currently implements the WebAssembly MVP. Further reads here: https://medium.com/graalvm/announcing-graalwasm-a-webassembly-engine-in-graalvm-25cd0400a7f2
As a conclusion..
As a wrap up, we can say that WebAssembly is a very young, but a very powerful technology. It is still in MVP stage, with a lot of specs yet to be implemented (like multithreading, SIMD, etc).
But even now WebAssembly can run with speeds up to 1.2x native. And even now we can offload many server-side activities to the client’s browser and avoid performance issues. And we are able to do this from Java world.
Additional resources:
My talk about Webassembly and Java https://www.youtube.com/watch?v=93z9SaLQVVw
Further reading about Wasm and graalVM https://github.com/neomatrix369/awesome-graal#wasm