Non-Goals

It is not a goal to provide a mechanism to represent all possible bytecodes, provide fine-grained control over code generation, or provide a translation specification from Java source code to Java classfiles. It is not a goal to extend the treatment of the ConstantValue attribute to support greater constant inlining across compilation units. It would be highly undesirable for this feature to introduce new language syntax.

Motivation

JSR-292 added the invokedynamic ("indy") bytecode and several new constant pool types for representing method types and method handles. Originally, these facilities were aimed at dynamically typed languages, but statically typed languages like Java have discovered ways to benefit from them as well (both lambda conversion and string concatenation in javac are translated using indy, and wider use is anticipated.) As more runtime functionality is expressed in terms of indy bootstraps, the inability to access this functionality from Java source code (including for purposes of testing these bootstraps) increasingly becomes an impediment. (This problem will get even worse with the introduction of "constant dynamic" (condy), the subject of a separate JEP.)

Code that uses the new constant pool forms -- MethodType and MethodHandle -- typically initialized them statically, to avoid the cost of lazy initialization, but the cost of this is extra work (including loading of classes) at class initialization time. The JVM already provides a mechanism to efficiently lazily initialize and intern shared constants -- the constant pool. Providing a means to nominally describe constants, and load them via LDC, means that Java developers can leverage this mechanism directly.

Further, a nominal form for complex constant pool types is needed anyway for bytecode APIs and compiler plugin APIs, which often have to reinvent them anew each time, such as ASMs Handle class. (And again, this problem will get worse with the introduction of "constant dynamic.")

Description

For every object that can be described via a constant pool entry, we want a companion object that describes that entry in terms only of other constants (as the constant pool does), where names of classes are interpreted relative to the class loader of the class in which they are resolved. (This means that one can create a description for a class that is not loaded, and that creating a descriptor does not trigger any class loading.) Some constant pool forms (such as Integer or String) can act as their own descriptor; for more complex constants (Class, MethodType) an explicit descriptor is needed.

Creating a ClassConstant merely validates the structure of the descriptor and stores it; no class loading takes place. More complex constant descriptors, such as MethodTypeConstant, use ClassConstant to describe its parameter and return types, and MethodHandleConstant uses ClassConstant and MethodTypeConstant, so that complex descriptors are "nominal all the way down."

Constant propagation

We enhance the set of variables and expressions that are considered to be constant expressions (CE), and for these constant expressions, the compiler tracks and propagates their values during compilation, and ultimately can use these propagated constants when they are used as arguments to intrinsified methods. We do not perform any new constant folding -- we merely track the value of any expressions that are CE according to the following rules, and if a CE is used as an argument to an intrinsic method, the constant value is used at the point of intrinsification.

int, long, float, double, and String literals are CE.

static final fields whose initializer is a CE are CE.

effectively final local variables whose initializer is a CE are CE.

For a suitable set of static factory methods in XxxConstant, when all arguments are CE, the result is CE. So for example, if we invoke ClassConstant.of() with the CE argument `"Lcom/Foo;", the result is CE.

For a suitable set of instance methods in XxxConstant, when all arguments are CE and the receiver is CE, then the result is CE. So for example, if we create a MethodHandleConstant via a factory with with only constant inputs, and then call its type() method, the result is a CE MethodTypeConstant.

LDC intrinsification

which the compiler will intrinsify, meaning that it will replace the invocation of the method with an actual LDC bytecode, with an operand corresponding to the constant described by constant. It will be a compile-time error if the Constable passed is not a CE (and the compiler can issue compile-time warnings if this constant or any of the constants indirectly referenced by it describe a class or member that is not present on the compile-time class path.) These intrinsic methods would most likely not be reflectively invocable.

Invokedynamic descriptors

An invokedynamic bootstrap is described by a structure similar to XxxConstant, for which the compiler also provide CE propagation:

Invokedynamic intrinsification

Again, the indy argument must be CE, or it is a compile-time error. The compiler intrinsifies calls to invokedynamic() to a real invokedynamic instruction, described by a BootstrapMethods entry corresponding to the BootstrapSpecifier. The invocation descriptor is copied from that of the BootstrapSpecifier, and arguments or return values are typed-checked and adapted against that signature.

Alternatives

JDK 7 explored direct syntactic support for indy, which was rejected because it added language complexity for a use case only needed by an extreme minority of Java developers.

We also considered deterministic constant folding, where initialization of Class and MethodHandle objects from constant inputs could be intrinsified, propagated, and folded by the compiler, but rejected this approach because the timing of side-effects was insufficiently transparent.

Dependencies

This feature interacts with "constant dynamic"; when that is available, additional support for dynamic constants will be required.

This feature would likely be useful to Isolated Methods (JDK-8158765).