Summary
java.lang.runtime.Carriers manages the creation of anonymous classes while maximizing the reuse of underlying classes.
Problem
The String Templates JEP draft proposes the introduction of a TemplatedString object for the primary purpose of carrying the template and associated values derived from a template literal. To avoid value boxing, early prototypes described these carrier objects using per-callsite anonymous classes shaped by value types, The use of distinct anonymous classes here is overkill, especially considering that many of these classes are similar in shape; containing one or two object fields and/or one or two integral fields. Pattern matching has a similar issue when carrying the values for the holes of a pattern. With potentially hundreds (thousands?) of template literals or patterns per application, we need to find an alternate approach for these value carriers.
Solution
The implementation should use a mechanism that decouples the strategy for carrier class generation from the client facility. One could implement one class per shape; one class for all shapes (with an Object[]), or something in the middle; having this decision behind a bootstrap means that it can be evolved at runtime, and optimized differently for different situations.
In general terms, the Carriers class simply caches anonymous classes keyed on shape. To further increase similarity in shape, the ordering of value types is handled by the API and not in the underlying anonymous class. If one client requires an object with one object value and one integer value and a second client inverts the request as an object with one integer value and one object value, then both clients may end up using the same underlying anonymous class. Further, types are folded as either integer (byte, short, int, boolean, char, float), long (long, double) or object. [We've seen that the performance hit by folding the long group into the integer group is significant, hence the separate group.]
Examples:
import java.lang.runtime.Carriers;
import java.lang.runtime.Carriers.CarrierFactory;
import java.lang.runtime.Carriers.CarrierElements;
...
// Create a carrier for a string and an integer
CarrierElements elements = CarrierFactory.of(String.class, int.class);
// Fetch the carrier constructor MethodHandle
MethodHandle constructor = elements.constructor();
// Fetch the list of carrier component MethodHandles
List<MethodHandle> components = elements.components();
// Create an instance of the carrier with a string and an integer
Object carrier = constructor.invokeExact("abc", 10);
// Extract the first component, type string
String string = (String)components.get(0).invokeExact(carrier);
// Extract the second component, type int
int i = (int)components.get(1).invokeExact(carrier);
Alternatively, the client can use static methods when the carrier use is scattered. This is possible since Carriers ensures that the same underlying carrier class is used when the same component types are provided.
// Describe carrier using a MethodType
MethodType mt = MethodType.methodType(Object.class, String.class, int.class);
// Fetch the carrier constructor MethodHandle
MethodHandle constructor = Carriers.constructor(mt);
// Fetch the list of carrier component MethodHandles
List<MethodHandle> components = Carriers.components(mt);
Specification
/**
* A <em>carrier</em> is an opaque object that can be used to store component values
* while avoiding primitive boxing associated with collection objects. Component values
* can be primitive or Object.
* <p>
* Clients can create new carrier instances by describing a carrier <em>shape</em>, that
* is, a {@linkplain MethodType method type} whose parameter types describe the types of
* the carrier component values, or by providing the parameter types directly.
*
* {@snippet :
* // Create a carrier for a string and an integer
* CarrierElements elements = CarrierFactory.of(String.class, int.class);
* // Fetch the carrier constructor MethodHandle
* MethodHandle constructor = elements.constructor();
* // Fetch the list of carrier component MethodHandles
* List<MethodHandle> components = elements.components();
*
* // Create an instance of the carrier with a string and an integer
* Object carrier = constructor.invokeExact("abc", 10);
* // Extract the first component, type string
* String string = (String)components.get(0).invokeExact(carrier);
* // Extract the second component, type int
* int i = (int)components.get(1).invokeExact(carrier);
* }
*
* Alternatively, the client can use static methods when the carrier use is scattered.
* This is possible since {@link Carriers} ensures that the same underlying carrier
* class is used when the same component types are provided.
*
* {@snippet :
* // Describe carrier using a MethodType
* MethodType mt = MethodType.methodType(Object.class, String.class, int.class);
* // Fetch the carrier constructor MethodHandle
* MethodHandle constructor = Carriers.constructor(mt);
* // Fetch the list of carrier component MethodHandles
* List<MethodHandle> components = Carriers.components(mt);
* }
*
* @implNote The strategy for storing components is deliberately left unspecified
* so that future improvements will not be hampered by issues of backward compatibility.
*
* @since 19
*/
public final class Carriers {
/**
* Maximum number of components in a carrier (based on the maximum
* number of args to a constructor.)
*/
public static final int MAX_COMPONENTS = 255 - /* this */ 1;
/**
* This factory class generates {@link CarrierElements} instances containing the
* {@link MethodHandle MethodHandles} to the constructor and accessors of a carrier
* object.
* <p>
* Clients can create instances by describing a carrier <em>shape</em>, that
* is, a {@linkplain MethodType method type} whose parameter types describe the types of
* the carrier component values, or by providing the parameter types directly.
*/
public static class CarrierFactory {
/**
* Factory method to return a {@link CarrierElements} instance that matches the shape of
* the supplied {@link MethodType}. The return type of the {@link MethodType} is ignored.
*
* @param methodType {@link MethodType} whose parameter types supply the
* the shape of the carrier's components
*
* @return {@link CarrierElements} instance
*
* @throws NullPointerException is methodType is null
* @throws IllegalArgumentException if number of component slots exceeds maximum
*/
public static CarrierElements of(MethodType methodType) ...
/**
* Factory method to return a {@link CarrierElements} instance that matches the shape of
* the supplied parameter types.
*
* @param ptypes parameter types that supply the shape of the carrier's components
*
* @return {@link CarrierElements} instance
*
* @throws NullPointerException is ptypes is null
* @throws IllegalArgumentException if number of component slots exceeds maximum
*/
public static CarrierElements of(Class < ? >...ptypes) ...
}
/**
* Instances of this class provide the {@link MethodHandle MethodHandles} to the
* constructor and accessors of a carrier object. The original component types can be
* gleaned from the parameter types of the constructor {@link MethodHandle} or by the
* return types of the components' {@link MethodHandle MethodHandles}.
*/
public static class CarrierElements {
/**
* {@return the underlying carrier class}
*/
public Class<?> carrierClass() ...
/**
* {@return the constructor {@link MethodHandle} for the carrier. The
* carrier constructor will always have a return type of {@link Object} }
*/
public MethodHandle constructor() ...
/**
* {@return immutable list of component accessor {@link MethodHandle MethodHandles}
* for all the carrier's components. The receiver type of the accessors
* will always be {@link Object} }
*/
public List<MethodHandle> components() ...
/**
* {@return a component accessor {@link MethodHandle} for component {@code i}.
* The receiver type of the accessor will be {@link Object} }
*
* @param i component index
*
* @throws IllegalArgumentException if {@code i} is out of bounds
*/
public MethodHandle component(int i) ...
}
/**
* {@return the underlying carrier class of the carrier representing {@code methodType} }
*
* @param methodType {@link MethodType} whose parameter types supply the the shape of the
* carrier's components
*
* @implNote Used internally by Condy APIs.
*/
public static Class<?> carrierClass(MethodType methodType) ...
/**
* {@return the constructor {@link MethodHandle} for the carrier representing {@code
* methodType}. The carrier constructor will always have a return type of {@link Object} }
*
* @param methodType {@link MethodType} whose parameter types supply the the shape of the
* carrier's components
*
* @implNote Used internally by Condy APIs.
*/
public static MethodHandle constructor(MethodType methodType) ...
/**
* {@return immutable list of component accessor {@link MethodHandle MethodHandles} for
* all the components of the carrier representing {@code methodType}. The receiver type of
* the accessors will always be {@link Object} }
*
* @param methodType {@link MethodType} whose parameter types supply the the shape of the
* carrier's components
*
* @implNote Used internally by Condy APIs.
*/
public static List<MethodHandle> components(MethodType methodType) ...
/**
* {@return a component accessor {@link MethodHandle} for component {@code i} of the
* carrier representing {@code methodType}. The receiver type of the accessor will always
* be {@link Object} }
*
* @param methodType {@link MethodType} whose parameter types supply the the shape of the
* carrier's components
* @param i component index
*
* @implNote Used internally by Condy APIs.
*
* @throws IllegalArgumentException if {@code i} is out of bounds
*/
public static MethodHandle component(MethodType methodType, int i) ...
}
- csr of
-
JDK-8282798 java.lang.runtime.Carrier
-
- Closed
-