Monday 31 December 2012

The long and winding road of JSR 292

As the biggest change to the JVM since the introduction of Java, I've been acutely aware that I can't ignore JSR 292 and simply leave it unimplemented in JamVM.  This has been reinforced recently by announcements such as Nashorn that indicate that more and more developments in the future will require JSR 292.

However, my record of implementing it has been dismal.  I first started about this time last year (end of December 2011).  At the time I hoped to get it finished for FOSDEM in February.  This was a tall order, but by FOSDEM I had invokeExact working, and could run several simple examples (all with OpenJDK 7).  I understood the general framework, and the way in which method handles were chained together (invocation involved following the chain, performing transformations along the way, until the final target method was reached).

The problem was the number of transformations the VM needed to implement.  Certain ones such as unboxing (REF_TO_PRIM) and retyping were straight-forward.  But more complex ones such as argument spreading were extremely time consuming, and there were even more exotic ones requiring "ricochet frames" (I never did work out exactly what they were).  Anyway, I didn't deliberately abandon the work, I just stopped and didn't touch JamVM for 6 months.

The breakthrough came from reading Roman Kennke's blog of getting Zero working with OpenJDK 8.  Of particular interest was the removal of the adapter code from Zero (like JamVM, it was incomplete).  It was no longer needed as the transformations are now done in Java, via LambdaForms.  These are compiled into bytecodes via the JSR 292 runtime, again coded in Java.

So the main stumbling block of my last attempt was gone.  However, unlike with Zero, in addition to invokedynamic the rest of the JSR 292 support within the VM has to be implemented (class file changes, resolution of method handles/types, call site bootstrap methods, stack walking, field injection, etc.).  It's taken a couple of weeks work, with a lot of debugging over Christmas (it's been so wet in the UK there's been nothing much else to do) but all but one of the jtreg tests for java.lang.invoke pass.  In fact, all tests were passing until I updated my sources a few days ago and found an extra 6 tests.
Passed: java/lang/invoke/6987555/Test6987555.java
Passed: java/lang/invoke/6991596/Test6991596.java
Passed: java/lang/invoke/6998541/Test6998541.java
Passed: java/lang/invoke/7157574/Test7157574.java
Passed: java/lang/invoke/7196190/ClassForNameTest.java
FAILED: java/lang/invoke/7196190/GetUnsafeTest.java
Passed: java/lang/invoke/7196190/MHProxyTest.java
Passed: java/lang/invoke/lambda/LambdaAccessControlDoPrivilegedTest.java
Passed: java/lang/invoke/lambda/LambdaAccessControlTest.java
Passed: java/lang/invoke/AccessControlTest.java
Passed: java/lang/invoke/BigArityTest.java
Passed: java/lang/invoke/CallSiteTest.java
Passed: java/lang/invoke/ClassValueTest.java
Passed: java/lang/invoke/InvokeDynamicPrintArgs.java
Passed: java/lang/invoke/InvokeGenericTest.java
Passed: java/lang/invoke/JavaDocExamplesTest.java
Passed: java/lang/invoke/MethodHandlesTest.java
Passed: java/lang/invoke/MethodTypeTest.java
Passed: java/lang/invoke/PermuteArgsTest.java
Passed: java/lang/invoke/PrivateInvokeTest.java
Passed: java/lang/invoke/RicochetTest.java
Passed: java/lang/invoke/ThrowExceptionsTest.java
Test results: passed: 21; failed: 1
The changes aren't pushed to git yet, as JamVM will currently only work with OpenJDK 8.  Getting it to work with OpenJDK 6/7 (without JSR 292) won't be difficult, but GNU Classpath will be more tricky.