Thursday, April 4, 2019

New Valhalla Developments: Forwarders and Poxes

In the post "Updated VM-bridges document" on the valhalla-spec-experts OpenJDK mailing list, Brian Goetz provides, "an updated doc on forwarding-reversing bridges in the VM." This was a topic at the late March 2019 Burlington (Massachusetts) Valhalla Offsite. A nice summary of Burlington Valhalla Offsite is provided by Karen Kinnear, in which she records that Goetz discussed "migration" using "field bridges" and "method bridges" as part of L20 phase of the new Valhalla phasing approach. Goetz's post adds significant background details. In this post, I reference a few of the more interesting points made in these posts, but both posts provide significantly more details than I'm providing here and are worth reading for anyone interested in the direction of Project Valhalla.

Forwarders

Although Goetz spends the majority of his post discussing the "migration aspects" of "forwarding+reversing bridges" and the of "forwarders," I found the historical background he provided in the introductory paragraphs to be a clean, concise summary of how we got to where we are now. Here are some brief statements from this historical section of the post that stood out to me (quotes here are very slightly modified for emphasis and to provide links):

  • "In the Java 1.0 days, `javac` was little more than an 'assembler' for the classfile format, translating source code to bytecode in a mostly 1:1 manner."
  • "Over time, we've seen small divergences between the language model and the classfile model, and each of these is a source of sharp edges."
  • "In Java 1.1 the addition of inner classes, and the mismatch between the accessibility model in the language and the JVM (the language treated a nest as a single entity; the JVM treat nest members as separate classes) required "access bridges" (`access$000` methods), which have been the source of various issues over the years."
  • "Twenty years later, these methods were obviated by Nest-based Access Control [jep181] -- which represents the choice to align the VM model to the language model"
  • "In Java 5, while we were able to keep the translation largely stable and transparent through the use of erasure..."
  • "... there was one point of misalignment; several situations ... could give rise to the situation where two or more method descriptors -- which the JVM treats as distinct methods -- are treated by the language as if they correspond to the same method."
  • "To fool the VM, the compiler emits "bridge methods" which forward invocations from one signature to another. And, as often happens when we try to fool the VM, it ultimately has its revenge."
  • "Java 5 introduced the ability to override a method but to provide a more specific return type. (Java 8 later extended this to bridges in interfaces as well.)"

After providing the historical background and summarizing the issues associated with these historical developments, Goetz moves onto to providing significantly more details about "bridge methods." He discusses the "anatomy of a bridge method" (forwarding/invokevirtual) and points out that "bridges are brittle" because "separate compilation can move bridges from where already-compiled code expects them to be to places it does not expect them." I particularly like Goetz's concise summary of the fundamental brittleness problem with bridge methods:

The basic problem with bridge methods is that the language views the two method descriptors as two faces of the same actual method, whereas the JVM sees them as distinct methods. (And, reflection also has to participate in the charade.)

Goetz discusses in his post the "limits of bridges" and points out that their limits are mostly encountered when attempting to migrate in a binary compatible fashion:

The problem of migration arises both from language evolution (Valhalla aims to enable compatible migrating from value-based classes to value types, and from erased generics to specialized), as well as from the ordinary evolution of libraries.

After providing some specific examples of where bridge methods fail migration attempts for fields and overridden methods, Goetz introduces "fowarders":

In this document, we attempt to learn from the history of bridges, and create a new mechanism -- "forwarders" -- that work with the JVM instead of against it. This raises the level of expressivity of classfiles and opens the possibility of greater laziness. It is possible that traditional bridging scenarios can eventually be handled by forwarders too...

Goetz devotes the remainder of the post to specifics of forwarders such as their invocation, using forwarders with fields, overriding forwarders, adaptation of forwarders, and type checking and corner cases associated with forwarders.

Poxes: Value Type Wrappers for Primitives

There was more discussed in the Valhalla Offsite than forwarding. One of Kinnear's notes that stood out to me stated, "Poxes: value type wrappers for primitives." Since the introduction of "autoboxing and unboxing" in JDK 1.5, Java developers have become comfortable with the conceptual relationship of "boxed"/"wrapper" reference types to their primitive counterparts. Therefore, it does make some sense to me to use similar terminology for the concept of value type "wrappers" of primitive counterparts being called "poxes." Goetz describes "poxing" in the post "Finding the spirit of L-World" and calls "creating a value box for primitives" "a 'pox'" and discusses the possibility of "adjust[ing] the compiler's boxing behavior (when boxing to `Object` or an interface)" to "prefer the pox to the box."

Updating Valhalla Value Types Phases

In her post "Updated phasing of Valhalla Value Types," Kinnear outlines Valhalla value types "Phases/AIs for Proposals" discussed at the Burlington Valhalla offsite meeting mentioned earlier. She calls out one major timing proposal in its own paragraph (I added the emphasis):

One important phasing is moving null-default value types and migrating value-based-classes to null-default value types was moved out to L20, which is after the initial preview.

Kinnear provides a list of "phases/APIs" categorized under one of three general milestone allocations: L10, L20, and L100. The two specific topics covered in my post (forwarding and poxes) appear to be currently proposed for L20.

Conclusion

Valhalla's value types are still a ways out before we'll see them in a General Availability JDK, but I appreciate the significant effort being invested in these value types and look forward to benefiting from them at some point in the future.

1 comment:

@DustinMarx said...

The notes from the 10 April 2019 Valhalla EG meeting are now available. Notes include coverage of discussion as a follow-up to the Valhalla offsite meeting discussed in the above blog post. Under the "Meaning of the 'L' descriptor" heading, there is a definition stated: "'L' descriptor is: 'by pointer', nullable, not flattenable, not pre-load". There is also mention that e-mail consensus for the name to use for "value class" is "inline" (not to be confused with Kotlin's experimental inline classes), but there is still more discussion on that name in the notes. The notes also highlight discussion regarding "RefObject vs. ValObject."