Sunday, May 08, 2022

Jacobin JVM project after nine months

After nine months, Jacobin has been steadily moving forward. The biggest news of this quarter is that Spencer Uresk (@suresk on Twitter and suresk on GitHub) has joined the project. He's made his presence felt right away by rewriting how Jacobin loads JDK classes at start-up. Previously, we provided a curated set of JDK classes in the Jacobin distribution. These classes were searched for in the directory specified by JACOBIN_HOME. Spencer's improvement is that the classes are now loaded directly from the Java distribution on the runtime system. This means that if your system already has a JDK installed on it, all you need to run Jacobin is the single Jacobin executable file. Spencer has now turned his attention to running JAR files (because at present, Jacobin runs only individual class files).

While Spencer's working on that, I (Andrew Binstock) am continuing the work on the bytecode interpreter. Work is slow but steady and I aim to have it mostly complete by the end of this three-month cycle.

As of this quarter, we are compatible with classes through Java 17 (previously only through Java 11). We don't enforce sealed classes (Java 17's big new feature) but we can execute our test classes from Java 17 just fine.

By the numbers

Project size has risen from 17,588 lines in our codebase to 19,173, which consists of 6,383 lines of production code and 12,970 lines in 248 tests--a 2.003x ratio of test code to production code. We'll look to increase that ratio as we move forward. (It was at 1.4x at the three-month mark, and 2.10x at the six-month mark.)

How you can help

If you're interested in this project and you're on Github, we'd love a star. Knowing people are interested in Jacobin really helps keep our motivation and spirits high. If you're on Twitter, follow our handle (@jacobin_jvm). Thanks for your interest and support!

Friday, February 04, 2022

Jacobin JVM project after six months

After six months, Jacobin can now execute many of the most common bytecode instructions. Simple classes that use for-loops, call methods that compute values, and print the results to the screen work correctly. Several of these are now available in the testdata directory on GitHub. For example, Hello3.class performs the following:

public static void main( String[] args) {
int x;
for( int i = 0; i < 10; i++) {
    x = addTwo(i, i-1);
    System.out.println( x );
}
  }

  static int addTwo(int j, int k) {
int m = multTwo(j, k);
return m+1;
  }

  static int multTwo(int m, int n){
return m*n;
  }
}

What you're seeing is a loop that calls a method, which in turn calls another method. If you run this without any command-line options, it will print out the expected result (a series of integers ranging from 1 to 73). 

If you run it with -verbose:finest, Jacobin will present a wealth of information about what's going on inside the JVM. You can also do instruction-level tracing with -trace:inst. The last few lines of the instruction trace look like this:

class: Hello3, meth: main, pc: 20, inst: INVOKEVIRTUAL, tos: 1
class: Hello3, meth: main, pc: 23, inst: IINC, tos: -1
class: Hello3, meth: main, pc: 26, inst: GOTO, tos: -1
class: Hello3, meth: main, pc: 2, inst: ILOAD_2, tos: -1
class: Hello3, meth: main, pc: 3, inst: BIPUSH, tos: 0
class: Hello3, meth: main, pc: 5, inst: IF_ICMPGE, tos: 1
class: Hello3, meth: main, pc: 29, inst: RETURN, tos: -1

The class and method fields are self-explanatory. pc refers to the location of the bytecode instruction, inst: refers to the actual instruction, and tos: represents the top of the operand stack before the instruction (-1 = empty stack, 0 = 1 item on the stack, etc.)

When you run Jacobin, it loads some 1500 Java classes in the background (just like the JVM does). If you want to see the list of these classes, run Jacobin with the -verbose:class command-line option. (A tribute to both the go language implementation and the design of Java classes is that these 1500 classes can be read, parsed, and posted in less than 300ms.) 

Current work

We're presently working on object creation. (So far, all the test classes don't require the creation of new objects.) The next step will then be handling exceptions, and then running classes that are in separate source files. As we create test classes for these developments, we'll perforce be adding new bytecode instructions to our interpreter. 

By the numbers

As of February 1 (six months into the project) the project spans 52 files consisting of 17,588 lines. Our pipeline consists of 223 tests and 11,922 lines (code and data) making our testing corpus 2.10x the size of our production code. We'll be looking to increase this ratio going forward. (It was 1.4x at the three-month mark.)

How you can help

If you're interested in this project and you're on Github, we'd love a star. Knowing people are interested in Jacobin really helps keep our motivation and spirits high. If you're on Twitter, follow our handle (@jacobin_jvm). Thanks for your interest and support!