Blocks :
|
|
Relates :
|
|
Relates :
|
Summary ------- Expose intrinsified library support allowing Java developers to write Java source code that reliably translates into `INVOKEDYNAMIC` invocations, so they can directly access functionality implemented via `invokedynamic` bootstraps. Expose the ability to lazily initialize create runtime entities expressible as JVM constants (like `Class` and `MethodHandle`) without compromising performance. Enhance constant propagation to include descriptor types for relevant constant pool entry types, such as `Class`, `MethodType`, and `MethodHandle`. 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. public interface Constable<T> { T resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException; } public static final class ClassConstant implements Constable<Class> { private final String descriptor; private ClassConstant(String descriptor) { ... } public static ClassConstant of(String descriptor) { return new ClassConstant(descriptor); } public String descriptorString() { return descriptor; } public Class resolveConstant(MethodHandles.Lookup lookup) throws ReflectiveOperationException { ... } } 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 There will be some method corresponding to the `LDC` bytecode: class Intrinsics { public static<T> T ldc(Constable<T> constant) { ... } } 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: public static final class BootstrapSpecifier { final MethodHandleConstant bsm; final String invocationName; final MethodTypeConstant invocationDesc; final Constable<?>[] bsmArgs; private BootstrapSpecifier(MethodHandleConstant bsm, String invocationName, MethodTypeConstant invocationDesc, Constable<?>... bsmArgs) { this.bsm = bsm; this.invocationName = invocationName; this.invocationDesc = invocationDesc; this.bsmArgs = bsmArgs; } public static BootstrapSpecifier of(MethodHandleConstant bsm, String invocationName, MethodTypeConstant invocationDesc, Constable<?>... bsmArgs) { return new BootstrapSpecifier(bsm, invocationName, invocationDesc, bsmArgs); } } ### Invokedynamic intrinsification Corresponding to `ldc()`, there is an intrinsic for `invokedynamic`: class Intrinsics { @PolymorphicSignature public static Object invokedynamic(BootstrapSpecifier indy, Object... args) { return null; } } 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).
|