Tuesday, August 09, 2022

Jacobin JVM At The 1-year Mark

 After 12 months, Jacobin, the more than minimalist JVM written in Go, has come quite far. Presently, it can execute simple Java classes and JARs and can do several interesting things, described shortly. The source code, which is available under the Mozilla open source licence and housed on GitHub, contains several sample Java classes that demonstrate the kinds of classes Jacobin can execute accurately and quickly.

Jacobin responds to most of the options listed in java -help, including supporting a range of verbosity options that can log considerable data to the console as the program is running. For a huge amount of output, use -verbose:finest switch. You can even do instruction-level tracing with the -trace:inst command-line switch.

Jacobin is a single executable with no dependencies. It requires only a JDK distribution on the local machine. Any JDK through Java 17 will work.

Under the covers, Jacobin—like OpenJDK-based JVMs—loads some 1,400 classes in the background. These comprise all the basic classes of the Java distribution. The class loaders in Jacobin perform a detailed parse and format-check of the app classes, with linking and preparation done on-the-fly at execution time.

What’s Next?

The team of Spencer Uresk (@suresk) and Andrew Binstock (@platypusguy) are working primarily in the following areas: completion of the bytecode interpreter (mostly to be completed by Andrew) and designing and developing an observability client, mostly by Spencer. (Observability is the ability to see what’s happening inside the JVM.) The README page on GitHub gives the current status of the various subsystems under development.

By the Numbers

After one year, the Jacobin codebase consists of 21,051 lines (including comments and blank lines). Of those, 14,499 lines make up 291 tests, meaning that the testing code is presently 2.21x the size of the production code. We strive to increase that multiple. The unit tests cover 72% of the production code, while the integration tests cover even more.

This deep commitment to testing is crucial to advancing the project. To move beyond running just the simplest of classes, Jacobin must adopt a lot of the inner complexity of the JVM. Debugging the interactions of many interlocking parts is nobody’s idea of fun. So, for our own peace of mind, we invest heavily in making sure that the code we write works exactly as we expect. And, of course, this also leads to a good user experience.

Easy Things You Can Do to Help

While Jacobin is still in pre-alpha mode, if you choose to build it or run one of the posted executables on GitHub, we’d love your feedback. We respond quickly to any and all feedback and questions. If, instead, you’d just like to show your support for the project, we'd love a star on GitHub. Knowing people are interested in Jacobin really helps keep our motivation and spirits high. If you're on Twitter, please follow our handle (@jacobin_jvm) to keep abreast of what we’re doing.

Thank you for your interest. Onward to year 2!

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!