DomWilliams0/zig-jvm
Hotspot-compatible JVM in Zig
A Java Virtual Machine implementation in Zig.
This makes use of the system Java class files from OpenJDK 18
, and reimplements all native code.
Linux only for now.
libffi
)JNIEnv*
passed to native functionssynchronized
methods(🚧 = in progress or planned)
A snippet from the logs to show the current capabilities:
...
debug: executing java/lang/Throwable.fillInStackTrace
debug: call stack:
* 0) java/lang/Throwable.fillInStackTrace (pc=0)
* 1) java/lang/Throwable.<init> (pc=24)
* 2) java/lang/Exception.<init> (pc=2)
* 3) java/io/IOException.<init> (pc=2)
* 4) java/io/FileNotFoundException.<init> (pc=2)
* 5) Throw.vmTest (pc=6)
debug: operand stack: {}
debug: local vars: [#0: reference, java/io/FileNotFoundException@7f4ab77b7f00]
debug: pc=0: aload_0
debug: operand stack: pushed #1 (reference): java/io/FileNotFoundException@7f4ab77b7f00
debug: operand stack: {#0: reference, java/io/FileNotFoundException@7f4ab77b7f00}
debug: local vars: [#0: reference, java/io/FileNotFoundException@7f4ab77b7f00]
debug: pc=1: getfield
debug: resolving class java/lang/Throwable
debug: operand stack: popped #0 (reference): java/io/FileNotFoundException@7f4ab77b7f00
debug: operand stack: pushed #1 (reference): [java/lang/StackTraceElement@7f4ab780ce00
debug: getfield(java/io/FileNotFoundException@7f4ab77b7f00, stackTrace) = [java/lang/StackTraceElement@7f4ab780ce00
debug: operand stack: {#0: reference, [java/lang/StackTraceElement@7f4ab780ce00}
debug: local vars: [#0: reference, java/io/FileNotFoundException@7f4ab77b7f00]
debug: pc=4: ifnonnull
debug: operand stack: popped #0 (reference): [java/lang/StackTraceElement@7f4ab780ce00
debug: operand stack: {}
debug: local vars: [#0: reference, java/io/FileNotFoundException@7f4ab77b7f00]
debug: pc=14: aload_0
debug: operand stack: pushed #1 (reference): java/io/FileNotFoundException@7f4ab77b7f00
debug: operand stack: {#0: reference, java/io/FileNotFoundException@7f4ab77b7f00}
debug: local vars: [#0: reference, java/io/FileNotFoundException@7f4ab77b7f00]
debug: pc=15: iconst_0
debug: operand stack: pushed #2 (int): 0
debug: operand stack: {#0: reference, java/io/FileNotFoundException@7f4ab77b7f00, #1: int, 0}
debug: local vars: [#0: reference, java/io/FileNotFoundException@7f4ab77b7f00]
debug: pc=16: invokevirtual
debug: resolving class java/lang/Throwable
debug: resolved method to java/io/FileNotFoundException.fillInStackTrace
debug: executing java/io/FileNotFoundException.fillInStackTrace
debug: binding native method
debug: looking for 'Java_java_lang_Throwable_fillInStackTrace'
debug: call stack:
* 0) java/io/FileNotFoundException.fillInStackTrace (native)
* 1) java/lang/Throwable.fillInStackTrace (pc=16)
* 2) java/lang/Throwable.<init> (pc=24)
* 3) java/lang/Exception.<init> (pc=2)
* 4) java/io/IOException.<init> (pc=2)
* 5) java/io/FileNotFoundException.<init> (pc=2)
* 6) Throw.vmTest (pc=6)
Please note that this is:
0.13.0-dev.365+332fbb4b0
).The way I am progressing through the massive amount of functionality expected from a JVM is to build
up a supply of small programs that exercise different parts of the JVM. These programs are in
src/test
, and can be run as follows:
jimage extract --dir $EXTRACT_DIR /usr/lib/jvm/java-18-openjdk/lib/modules
java.base/java/lang/Object.class
existszig build run-testrunner -- -Xbootclasspath $EXTRACT_DIR/java.base
If you're feeling brave, you can run a given class file, just like the normal java
command. Don't
expect it to work though.
If Test.class
is in $CLASS_DIR
, then run
zig build run-java -- -Xbootclasspath $EXTRACT_DIR:$CLASS_DIR Test
First of all, why not? This is a great technical project that constantly stretches me.
Also this is my second iteration of implementing a JVM, the first is in Rust and is still pretty incomplete, but suffers from some over-the-top type safety that I wanted to reduce in a second iteration, which would also help with performance.