jdk/src/java.base/share/classes/java/lang/runtime/Carriers.java
Jim Laskey 6af0af5934 8310913: Move ReferencedKeyMap to jdk.internal so it may be shared
Reviewed-by: naoto, rriggs, mchung, liach
2023-07-31 19:11:14 +00:00

1005 lines
35 KiB
Java

/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.runtime;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ReferencedKeyMap;
import static java.lang.invoke.MethodType.methodType;
/**
* 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 initializingConstructor = elements.initializingConstructor();
* // 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 = initializingConstructor.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 21
*
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
* Do not rely on its availability.
*/
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;
/**
* Number of integer slots used by a long.
*/
static final int LONG_SLOTS = Long.SIZE / Integer.SIZE;
/*
* Initialize {@link MethodHandle} constants.
*/
static {
try {
Lookup lookup = MethodHandles.lookup();
FLOAT_TO_INT = lookup.findStatic(Float.class, "floatToRawIntBits",
methodType(int.class, float.class));
INT_TO_FLOAT = lookup.findStatic(Float.class, "intBitsToFloat",
methodType(float.class, int.class));
DOUBLE_TO_LONG = lookup.findStatic(Double.class, "doubleToRawLongBits",
methodType(long.class, double.class));
LONG_TO_DOUBLE = lookup.findStatic(Double.class, "longBitsToDouble",
methodType(double.class, long.class));
} catch (ReflectiveOperationException ex) {
throw new AssertionError("carrier static init fail", ex);
}
}
/*
* float/double conversions.
*/
private static final MethodHandle FLOAT_TO_INT;
private static final MethodHandle INT_TO_FLOAT;
private static final MethodHandle DOUBLE_TO_LONG;
private static final MethodHandle LONG_TO_DOUBLE;
/**
* Given an initializer {@link MethodHandle} recast and reorder arguments to
* match shape.
*
* @param carrierShape carrier shape
* @param initializer carrier constructor to reshape
*
* @return constructor with arguments recasted and reordered
*/
static MethodHandle reshapeInitializer(CarrierShape carrierShape,
MethodHandle initializer) {
int count = carrierShape.count();
Class<?>[] ptypes = carrierShape.ptypes();
int objectIndex = carrierShape.objectOffset() + 1;
int intIndex = carrierShape.intOffset() + 1;
int longIndex = carrierShape.longOffset() + 1;
int[] reorder = new int[count + 1];
Class<?>[] permutePTypes = new Class<?>[count + 1];
MethodHandle[] filters = new MethodHandle[count + 1];
boolean hasFilters = false;
permutePTypes[0] = CarrierObject.class;
reorder[0] = 0;
int index = 1;
for (Class<?> ptype : ptypes) {
MethodHandle filter = null;
int from;
if (!ptype.isPrimitive()) {
from = objectIndex++;
ptype = Object.class;
} else if (ptype == double.class) {
from = longIndex++;
filter = DOUBLE_TO_LONG;
} else if (ptype == float.class) {
from = intIndex++;
filter = FLOAT_TO_INT;
} else if (ptype == long.class) {
from = longIndex++;
} else {
from = intIndex++;
ptype = int.class;
}
permutePTypes[index] = ptype;
reorder[from] = index++;
if (filter != null) {
filters[from] = filter;
hasFilters = true;
}
}
if (hasFilters) {
initializer = MethodHandles.filterArguments(initializer, 0, filters);
}
MethodType permutedMethodType =
methodType(initializer.type().returnType(), permutePTypes);
initializer = MethodHandles.permuteArguments(initializer,
permutedMethodType, reorder);
initializer = MethodHandles.explicitCastArguments(initializer,
methodType(CarrierObject.class, ptypes).insertParameterTypes(0, CarrierObject.class));
return initializer;
}
/**
* Given components array, recast and reorder components to match shape.
*
* @param carrierShape carrier reshape
* @param components carrier components to reshape
*
* @return list of components reshaped
*/
static List<MethodHandle> reshapeComponents(CarrierShape carrierShape,
MethodHandle[] components) {
int count = carrierShape.count();
Class<?>[] ptypes = carrierShape.ptypes();
MethodHandle[] reorder = new MethodHandle[count];
int objectIndex = carrierShape.objectOffset();
int intIndex = carrierShape.intOffset();
int longIndex = carrierShape.longOffset();
int index = 0;
for (Class<?> ptype : ptypes) {
MethodHandle component;
if (!ptype.isPrimitive()) {
component = components[objectIndex++];
} else if (ptype == double.class) {
component = MethodHandles.filterReturnValue(
components[longIndex++], LONG_TO_DOUBLE);
} else if (ptype == float.class) {
component = MethodHandles.filterReturnValue(
components[intIndex++], INT_TO_FLOAT);
} else if (ptype == long.class) {
component = components[longIndex++];
} else {
component = components[intIndex++];
}
MethodType methodType = methodType(ptype, CarrierObject.class);
reorder[index++] =
MethodHandles.explicitCastArguments(component, methodType);
}
return List.of(reorder);
}
/**
* Factory for carriers that are backed by long[] and Object[].
*/
static final class CarrierObjectFactory {
/**
* Unsafe access.
*/
private static final Unsafe UNSAFE;
/*
* Constructor accessor MethodHandles.
*/
private static final MethodHandle CONSTRUCTOR;
private static final MethodHandle GET_LONG;
private static final MethodHandle PUT_LONG;
private static final MethodHandle GET_INTEGER;
private static final MethodHandle PUT_INTEGER;
private static final MethodHandle GET_OBJECT;
private static final MethodHandle PUT_OBJECT;
static {
try {
UNSAFE = Unsafe.getUnsafe();
Lookup lookup = MethodHandles.lookup();
CONSTRUCTOR = lookup.findConstructor(CarrierObject.class,
methodType(void.class, int.class, int.class));
GET_LONG = lookup.findVirtual(CarrierObject.class, "getLong",
methodType(long.class, int.class));
PUT_LONG = lookup.findVirtual(CarrierObject.class, "putLong",
methodType(CarrierObject.class, int.class, long.class));
GET_INTEGER = lookup.findVirtual(CarrierObject.class, "getInteger",
methodType(int.class, int.class));
PUT_INTEGER = lookup.findVirtual(CarrierObject.class, "putInteger",
methodType(CarrierObject.class, int.class, int.class));
GET_OBJECT = lookup.findVirtual(CarrierObject.class, "getObject",
methodType(Object.class, int.class));
PUT_OBJECT = lookup.findVirtual(CarrierObject.class, "putObject",
methodType(CarrierObject.class, int.class, Object.class));
} catch (ReflectiveOperationException ex) {
throw new AssertionError("carrier static init fail", ex);
}
}
/**
* Constructor builder.
*
* @param carrierShape carrier object shape
*
* @return {@link MethodHandle} to generic carrier constructor.
*/
MethodHandle constructor(CarrierShape carrierShape) {
int objectCount = carrierShape.objectCount();
int primitiveCount = carrierShape.primitiveCount();
MethodHandle constructor = MethodHandles.insertArguments(CONSTRUCTOR,
0, primitiveCount, objectCount);
return constructor;
}
/**
* Adds constructor arguments for each of the allocated slots.
*
* @param carrierShape carrier object shape
*
* @return {@link MethodHandle} to specific carrier constructor.
*/
MethodHandle initializer(CarrierShape carrierShape) {
int longCount = carrierShape.longCount();
int intCount = carrierShape.intCount();
int objectCount = carrierShape.objectCount();
MethodHandle initializer = MethodHandles.identity(CarrierObject.class);
// long array index
int index = 0;
for (int i = 0; i < longCount; i++) {
MethodHandle put = MethodHandles.insertArguments(PUT_LONG, 1, index++);
initializer = MethodHandles.collectArguments(put, 0, initializer);
}
// transition to int array index (double number of longs)
index *= LONG_SLOTS;
for (int i = 0; i < intCount; i++) {
MethodHandle put = MethodHandles.insertArguments(PUT_INTEGER, 1, index++);
initializer = MethodHandles.collectArguments(put, 0, initializer);
}
for (int i = 0; i < objectCount; i++) {
MethodHandle put = MethodHandles.insertArguments(PUT_OBJECT, 1, i);
initializer = MethodHandles.collectArguments(put, 0, initializer);
}
return initializer;
}
/**
* Utility to construct the basic accessors from the components.
*
* @param carrierShape carrier object shape
*
* @return array of carrier accessors
*/
MethodHandle[] createComponents(CarrierShape carrierShape) {
int longCount = carrierShape.longCount();
int intCount = carrierShape.intCount();
int objectCount = carrierShape.objectCount();
MethodHandle[] components =
new MethodHandle[carrierShape.ptypes().length];
// long array index
int index = 0;
// component index
int comIndex = 0;
for (int i = 0; i < longCount; i++) {
components[comIndex++] = MethodHandles.insertArguments(GET_LONG, 1, index++);
}
// transition to int array index (double number of longs)
index *= LONG_SLOTS;
for (int i = 0; i < intCount; i++) {
components[comIndex++] = MethodHandles.insertArguments(GET_INTEGER, 1, index++);
}
for (int i = 0; i < objectCount; i++) {
components[comIndex++] = MethodHandles.insertArguments(GET_OBJECT, 1, i);
}
return components;
}
/**
* Cache mapping {@link MethodType} to previously defined {@link CarrierElements}.
*/
private static final Map<MethodType, CarrierElements>
methodTypeCache = ReferencedKeyMap.create(false, ConcurrentHashMap::new);
/**
* Permute a raw constructor and component accessor {@link MethodHandle MethodHandles} to
* match the order and types of the parameter types.
*
* @param carrierShape carrier object shape
*
* @return {@link CarrierElements} instance
*/
CarrierElements carrier(CarrierShape carrierShape) {
return methodTypeCache.computeIfAbsent(carrierShape.methodType, (mt) -> {
MethodHandle constructor = constructor(carrierShape);
MethodHandle initializer = initializer(carrierShape);
MethodHandle[] components = createComponents(carrierShape);
return new CarrierElements(
carrierShape,
CarrierObject.class,
constructor,
reshapeInitializer(carrierShape, initializer),
reshapeComponents(carrierShape, components));
});
}
}
/**
* Wrapper object for carrier data. Instance types are stored in the {@code objects}
* array, while primitive types are recast to {@code int/long} and stored in the
* {@code primitives} array. Primitive byte, short, char, boolean and int are stored as
* integers. Longs and doubles are stored as longs. Longs take up the first part of the
* primitives array using normal indices. Integers follow using int[] indices offset beyond
* the longs using unsafe getInt/putInt.
*/
static class CarrierObject {
/**
* Carrier for primitive values.
*/
private final long[] primitives;
/**
* Carrier for objects;
*/
private final Object[] objects;
/**
* Constructor.
*
* @param primitiveCount slot count required for primitives
* @param objectCount slot count required for objects
*/
protected CarrierObject(int primitiveCount, int objectCount) {
this.primitives = createPrimitivesArray(primitiveCount);
this.objects = createObjectsArray(objectCount);
}
/**
* Create a primitives array of an appropriate length.
*
* @param primitiveCount slot count required for primitives
*
* @return primitives array of an appropriate length.
*/
private long[] createPrimitivesArray(int primitiveCount) {
return primitiveCount != 0 ? new long[(primitiveCount + 1) / LONG_SLOTS] : null;
}
/**
* Create a objects array of an appropriate length.
*
* @param objectCount slot count required for objects
*
* @return objects array of an appropriate length.
*/
private Object[] createObjectsArray(int objectCount) {
return objectCount != 0 ? new Object[objectCount] : null;
}
/**
* Compute offset for unsafe access to long.
*
* @param i index in primitive[]
*
* @return offset for unsafe access
*/
private static long offsetToLong(int i) {
return Unsafe.ARRAY_LONG_BASE_OFFSET +
(long)i * Unsafe.ARRAY_LONG_INDEX_SCALE;
}
/**
* Compute offset for unsafe access to int.
*
* @param i index in primitive[]
*
* @return offset for unsafe access
*/
private static long offsetToInt(int i) {
return Unsafe.ARRAY_LONG_BASE_OFFSET +
(long)i * Unsafe.ARRAY_INT_INDEX_SCALE;
}
/**
* Compute offset for unsafe access to object.
*
* @param i index in objects[]
*
* @return offset for unsafe access
*/
private static long offsetToObject(int i) {
return Unsafe.ARRAY_OBJECT_BASE_OFFSET +
(long)i * Unsafe.ARRAY_OBJECT_INDEX_SCALE;
}
/**
* {@return long value at index}
*
* @param i array index
*/
private long getLong(int i) {
return CarrierObjectFactory.UNSAFE.getLong(primitives, offsetToLong(i));
}
/**
* Put a long value into the primitive[].
*
* @param i array index
* @param value long value to store
*
* @return this object
*/
private CarrierObject putLong(int i, long value) {
CarrierObjectFactory.UNSAFE.putLong(primitives, offsetToLong(i), value);
return this;
}
/**
* {@return int value at index}
*
* @param i array index
*/
private int getInteger(int i) {
return CarrierObjectFactory.UNSAFE.getInt(primitives, offsetToInt(i));
}
/**
* Put a int value into the int[].
*
* @param i array index
* @param value int value to store
*
* @return this object
*/
private CarrierObject putInteger(int i, int value) {
CarrierObjectFactory.UNSAFE.putInt(primitives, offsetToInt(i), value);
return this;
}
/**
* {@return Object value at index}
*
* @param i array index
*/
private Object getObject(int i) {
return CarrierObjectFactory.UNSAFE.getReference(objects, offsetToObject(i));
}
/**
* Put a object value into the objects[].
*
* @param i array index
* @param value object value to store
*
* @return this object
*/
private CarrierObject putObject(int i, Object value) {
CarrierObjectFactory.UNSAFE.putReference(objects, offsetToObject(i), value);
return this;
}
}
/**
* Class used to tally and track the number of ints, longs and objects.
*
* @param longCount number of longs and doubles
* @param intCount number of byte, short, int, chars and booleans
* @param objectCount number of objects
*/
private record CarrierCounts(int longCount, int intCount, int objectCount) {
/**
* Count the number of fields required in each of Object, int and long.
*
* @param ptypes parameter types
*
* @return a {@link CarrierCounts} instance containing counts
*/
static CarrierCounts tally(Class<?>[] ptypes) {
return tally(ptypes, ptypes.length);
}
/**
* Count the number of fields required in each of Object, int and long
* limited to the first {@code n} parameters.
*
* @param ptypes parameter types
* @param n number of parameters to check
*
* @return a {@link CarrierCounts} instance containing counts
*/
private static CarrierCounts tally(Class<?>[] ptypes, int n) {
int longCount = 0;
int intCount = 0;
int objectCount = 0;
for (int i = 0; i < n; i++) {
Class<?> ptype = ptypes[i];
if (!ptype.isPrimitive()) {
objectCount++;
} else if (ptype == long.class || ptype == double.class) {
longCount++;
} else {
intCount++;
}
}
return new CarrierCounts(longCount, intCount, objectCount);
}
/**
* {@return total number of components}
*/
private int count() {
return longCount + intCount + objectCount;
}
/**
* {@return total number of slots}
*/
private int slotCount() {
return longCount * LONG_SLOTS + intCount + objectCount;
}
}
/**
* Constructor
*/
private Carriers() {
throw new AssertionError("private constructor");
}
/**
* Shape of carrier based on counts of each of the three fundamental data
* types.
*/
private static class CarrierShape {
/**
* {@link MethodType} providing types for the carrier's components.
*/
final MethodType methodType;
/**
* Counts of different parameter types.
*/
final CarrierCounts counts;
/**
* Constructor.
*
* @param methodType {@link MethodType} providing types for the
* carrier's components
*/
public CarrierShape(MethodType methodType) {
this.methodType = methodType;
this.counts = CarrierCounts.tally(methodType.parameterArray());
}
/**
* {@return number of long fields needed}
*/
int longCount() {
return counts.longCount();
}
/**
* {@return number of int fields needed}
*/
int intCount() {
return counts.intCount();
}
/**
* {@return number of object fields needed}
*/
int objectCount() {
return counts.objectCount();
}
/**
* {@return slot count required for primitives}
*/
int primitiveCount() {
return counts.longCount() * LONG_SLOTS + counts.intCount();
}
/**
* {@return array of parameter types}
*/
Class<?>[] ptypes() {
return methodType.parameterArray();
}
/**
* {@return number of components}
*/
int count() {
return counts.count();
}
/**
* {@return number of slots used}
*/
int slotCount() {
return counts.slotCount();
}
/**
* {@return index of first long component}
*/
int longOffset() {
return 0;
}
/**
* {@return index of first int component}
*/
int intOffset() {
return longCount();
}
/**
* {@return index of first object component}
*/
int objectOffset() {
return longCount() + intCount();
}
}
/**
* 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.
*/
static final class CarrierFactory {
/**
* Constructor
*/
private CarrierFactory() {
throw new AssertionError("private constructor");
}
private static final CarrierObjectFactory FACTORY = new CarrierObjectFactory();
/**
* 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
*/
static CarrierElements of(MethodType methodType) {
Objects.requireNonNull(methodType, "methodType must not be null");
MethodType constructorMT = methodType.changeReturnType(Object.class);
CarrierShape carrierShape = new CarrierShape(constructorMT);
int slotCount = carrierShape.slotCount();
if (MAX_COMPONENTS < slotCount) {
throw new IllegalArgumentException("Exceeds maximum number of component slots");
}
return FACTORY.carrier(carrierShape);
}
/**
* 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
*/
static CarrierElements of(Class<?>...ptypes) {
Objects.requireNonNull(ptypes, "ptypes must not be null");
return of(methodType(Object.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}.
*/
static final class CarrierElements {
/**
* Slot count required for objects.
*/
private final int objectCount;
/**
* Slot count required for primitives.
*/
private final int primitiveCount;
/**
* Underlying carrier class.
*/
private final Class<?> carrierClass;
/**
* Constructor {@link MethodHandle}.
*/
private final MethodHandle constructor;
/**
* Initializer {@link MethodHandle}.
*/
private final MethodHandle initializer;
/**
* List of component {@link MethodHandle MethodHandles}
*/
private final List<MethodHandle> components;
/**
* Constructor
*/
private CarrierElements() {
throw new AssertionError("private constructor");
}
/**
* Constructor
*/
CarrierElements(CarrierShape carrierShape,
Class<?> carrierClass,
MethodHandle constructor,
MethodHandle initializer,
List<MethodHandle> components) {
this.objectCount = carrierShape.objectCount();
this.primitiveCount = carrierShape.primitiveCount();
this.carrierClass = carrierClass;
this.constructor = constructor;
this.initializer = initializer;
this.components = components;
}
/**
* {@return slot count required for objects}
*/
int objectCount() {
return objectCount;
}
/**
* {@return slot count required for primitives}
*/
int primitiveCount() {
return primitiveCount;
}
/**
* {@return the underlying carrier class}
*/
Class<?> carrierClass() {
return carrierClass;
}
/**
* {@return the constructor {@link MethodHandle} for the carrier. The
* carrier constructor will always have a return type of {@link Object} }
*/
MethodHandle constructor() {
return constructor;
}
/**
* {@return the initializer {@link MethodHandle} for the carrier}
*/
MethodHandle initializer() {
return initializer;
}
/**
* Return the constructor plus initializer {@link MethodHandle} for the carrier.
* The {@link MethodHandle} will always have a return type of {@link Object}.
* @return the constructor plus initializer {@link MethodHandle}
*/
MethodHandle initializingConstructor() {
return MethodHandles.foldArguments(initializer, 0, 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} }
*/
List<MethodHandle> components() {
return 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
*/
MethodHandle component(int i) {
if (i < 0 || components.size() <= i) {
throw new IllegalArgumentException("i is out of bounds " + i +
" of " + components.size());
}
return components.get(i);
}
@Override
public String toString() {
return "Carrier" + constructor.type().parameterList();
}
}
/**
* {@return the underlying carrier class of the carrier representing {@code methodType} }
*
* @param methodType {@link MethodType} whose parameter types supply the shape of the
* carrier's components
*/
static Class<?> carrierClass(MethodType methodType) {
return CarrierFactory.of(methodType).carrierClass();
}
/**
* {@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 shape of the
* carrier's components
*/
static MethodHandle constructor(MethodType methodType) {
MethodHandle constructor = CarrierFactory.of(methodType).constructor();
constructor = constructor.asType(constructor.type().changeReturnType(Object.class));
return constructor;
}
/**
* {@return the initializer {@link MethodHandle} for the carrier representing {@code
* methodType}. The carrier initializer will always take an {@link Object} along with
* component values and a return type of {@link Object} }
*
* @param methodType {@link MethodType} whose parameter types supply the shape of the
* carrier's components
*/
static MethodHandle initializer(MethodType methodType) {
MethodHandle initializer = CarrierFactory.of(methodType).initializer();
initializer = initializer.asType(initializer.type()
.changeReturnType(Object.class).changeParameterType(0, Object.class));
return initializer;
}
/**
* {@return the combination {@link MethodHandle} of the constructor and initializer
* for the carrier representing {@code methodType}. The carrier constructor/initializer
* will always take the component values and a return type of {@link Object} }
*
* @param methodType {@link MethodType} whose parameter types supply the shape of the
* carrier's components
*/
static MethodHandle initializingConstructor(MethodType methodType) {
MethodHandle constructor = CarrierFactory.of(methodType).initializingConstructor();
constructor = constructor.asType(constructor.type().changeReturnType(Object.class));
return constructor;
}
/**
* {@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 shape of the
* carrier's components
*/
static List<MethodHandle> components(MethodType methodType) {
return CarrierFactory
.of(methodType)
.components()
.stream()
.map(c -> c.asType(c.type().changeParameterType(0, Object.class)))
.toList();
}
/**
* {@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 shape of the
* carrier's components
* @param i component index
*
* @throws IllegalArgumentException if {@code i} is out of bounds
*/
static MethodHandle component(MethodType methodType, int i) {
MethodHandle component = CarrierFactory.of(methodType).component(i);
component = component.asType(component.type().changeParameterType(0, Object.class));
return component;
}
}