/* * Written by Gil Tene and Martin Thompson, and released to the public domain, * as explained at http://creativecommons.org/publicdomain/zero/1.0/ */ package org.ObjectLayout; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; /** * Intrinsic objects (declared with the {@link org.ObjectLayout.Intrinsic @Intrinsic} annotation) may have * their layout within the containing object instance optimized by JDK implementations, such that access * to their content is faster, and avoids certain de-referencing steps. * <p> * The {@link org.ObjectLayout.IntrinsicObjects} class provides static methods for constructing objects * that are {@link org.ObjectLayout.Intrinsic @Intrinsic} to their containing class. Calls to * {@link org.ObjectLayout.IntrinsicObjects#constructWithin * constructWithin(String fieldName, Object containingObject)} (and variants) are used to construct and * initialize intrinsic objects associated with specific instance fields that are declared as * {@link org.ObjectLayout.Intrinsic @Intrinsic} in their containing class. * <p> * Some example of declaring an intrinsic object include: * <blockquote><pre> * public class Line { * private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); * // * // Simple intrinsic object declaration and initialization: * // * {@literal @}Intrinsic * private final Point endPoint1 = IntrinsicObjects.constructWithin(lookup, "endPoint1", this); * {@literal @}Intrinsic * private final Point endPoint2 = IntrinsicObjects.constructWithin(lookup, "endPoint2", this); * ... * } * * public class Octagon { * private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); * // * // Intrinsic object declaration and initialization for a StructuredArray member: * // * {@literal @}Intrinsic(length = 8) * private final StructuredArray<Point> points = IntrinsicObjects.constructWithin(lookup, "points", this); * ... * } * </pre></blockquote> * */ public final class IntrinsicObjects { // Prevent construction: private IntrinsicObjects() { } /** * Construct an intrinsic object at the given field within the containing object, using a default constructor. * <p> * The field specified in {@code fieldName} must be annotated with {@link org.ObjectLayout.Intrinsic @Intrinsic}, * and must be declared private and final. * * @param lookup The lookup object to use for accessing the field * @param fieldName The name of the field within the containing object * @param containingObject The object instance that will contain this intrinsic object * @param <T> The type of the intrinsic object being constructed * @return A reference to the the newly constructed intrinsic object */ public static <T> T constructWithin( MethodHandles.Lookup lookup, final String fieldName, final Object containingObject) { IntrinsicObjectModel<T> model = lookupModelFor(lookup, fieldName, containingObject); return model.constructWithin(containingObject); } /** * Construct an intrinsic object at the given field within the containing object, using the given * constructor and arguments. * <p> * The field specified in {@code fieldName} must be annotated with {@link org.ObjectLayout.Intrinsic @Intrinsic}, * and must be declared private and final. * * @param lookup The lookup object to use for accessing the field * @param fieldName The name of the field within the containing object * @param containingObject The object instance that will contain this intrinsic object * @param objectConstructor The constructor to be used in constructing the intrinsic object instance * @param args the arguments to be used with the objectConstructor * @param <T> The type of the intrinsic object being constructed * @return A reference to the the newly constructed intrinsic object */ public static <T> T constructWithin( MethodHandles.Lookup lookup, final String fieldName, final Object containingObject, final Constructor<T> objectConstructor, final Object... args) { IntrinsicObjectModel<T> model = lookupModelFor(lookup, fieldName, containingObject); return model.constructWithin(containingObject, objectConstructor, args); } /** * Construct an intrinsic object at the given field within the containing object, using the * constructor and arguments supplied in the given objectCtorAndArgs argument. * <p> * The field specified in {@code fieldName} must be annotated with {@link org.ObjectLayout.Intrinsic @Intrinsic}, * and must be declared private and final. * * @param lookup The lookup object to use for accessing the field * @param fieldName The name of the field within the containing object * @param containingObject The object instance that will contain this intrinsic object * @param objectCtorAndArgs The constructor and arguments to be used in constructing the * intrinsic object instance * @param <T> The type of the intrinsic object being constructed * @return A reference to the the newly constructed intrinsic object */ public static <T> T constructWithin( MethodHandles.Lookup lookup, final String fieldName, final Object containingObject, final CtorAndArgs<T> objectCtorAndArgs) { IntrinsicObjectModel<T> model = lookupModelFor(lookup, fieldName, containingObject); return model.constructWithin(containingObject, objectCtorAndArgs); } /** * Construct an intrinsic object at the given field within the containing object, using the * supplied {@link StructuredArrayBuilder}. This form of constructWithin() can only be used * to construct intrinsic objects that derive from {@link org.ObjectLayout.StructuredArray}. * <p> * The field specified in {@code fieldName} must be annotated with {@link org.ObjectLayout.Intrinsic @Intrinsic}, * and must be declared private and final. * * @param lookup The lookup object to use for accessing the field * @param fieldName The name of the field within the containing object * @param containingObject The object instance that will contain this intrinsic object * @param arrayBuilder The {@link StructuredArrayBuilder} instance to be used in constructing the array * @param <T> The type of the intrinsic object being constructed * @return A reference to the the newly constructed intrinsic object */ public static <T> T constructWithin( MethodHandles.Lookup lookup, final String fieldName, final Object containingObject, final StructuredArrayBuilder arrayBuilder) { IntrinsicObjectModel<T> model = lookupModelFor(lookup, fieldName, containingObject); return model.constructWithin(containingObject, arrayBuilder); } /** * Construct an intrinsic object at the given field within the containing object, using the * supplied {@link PrimitiveArrayBuilder}. This form of constructWithin() can only be used * to construct intrinsic objects that derive from {@link org.ObjectLayout.AbstractPrimitiveArray}. * <p> * The field specified in {@code fieldName} must be annotated with {@link org.ObjectLayout.Intrinsic @Intrinsic}, * and must be declared private and final. * * @param lookup The lookup object to use for accessing the field * @param fieldName The name of the field within the containing object * @param containingObject The object instance that will contain this intrinsic object * @param arrayBuilder The {@link PrimitiveArrayBuilder} instance to be used in constructing the array * @param <T> The type of the intrinsic object being constructed * @return A reference to the the newly constructed intrinsic object */ public static <T> T constructWithin( MethodHandles.Lookup lookup, final String fieldName, final Object containingObject, final PrimitiveArrayBuilder arrayBuilder) { IntrinsicObjectModel<T> model = lookupModelFor(lookup, fieldName, containingObject); return model.constructWithin(containingObject, arrayBuilder); } private static final ConcurrentHashMap<Class, HashMap<String, IntrinsicObjectModel>> modelsByClass = new ConcurrentHashMap<Class, HashMap<String, IntrinsicObjectModel>>(); private static HashMap<String, IntrinsicObjectModel> lookupModelsByFieldNameForClass( MethodHandles.Lookup lookup, Object containingObject) { Class c = containingObject.getClass(); HashMap<String, IntrinsicObjectModel> modelsByFieldName = modelsByClass.get(c); if (modelsByFieldName == null) { modelsByFieldName = new HashMap<String, IntrinsicObjectModel>(); // Populate modelsByString hash map with all @Intrinsic fields found in the class: populateWithClassIntrinsicModels(lookup, containingObject, modelsByFieldName); modelsByClass.put(c, modelsByFieldName); } return modelsByFieldName; } private static <T> void populateWithClassIntrinsicModels( MethodHandles.Lookup lookup, Object containingObject, HashMap<String, IntrinsicObjectModel> modelsByFieldName) { Class c = containingObject.getClass(); for (Field field : c.getDeclaredFields()) { if (field.getAnnotation(Intrinsic.class) != null) { IntrinsicObjectModel<T> objectModel = createModel(lookup, field); modelsByFieldName.put(field.getName(), objectModel); } } } private static <T> IntrinsicObjectModel<T> lookupModelFor( MethodHandles.Lookup lookup, String fieldName, Object containingObject) { HashMap<String, IntrinsicObjectModel> modelsByFieldName = lookupModelsByFieldNameForClass(lookup, containingObject); @SuppressWarnings("unchecked") IntrinsicObjectModel<T> objectModel = (IntrinsicObjectModel<T>) modelsByFieldName.get(fieldName); if (objectModel == null) { throw new IllegalArgumentException( "No @Intrinsic field named \"" + fieldName + "\" found in " + containingObject.getClass()); } return objectModel; } private static <T> IntrinsicObjectModel<T> createModel(MethodHandles.Lookup lookup, Field field) { @SuppressWarnings("unchecked") Class<T> objectClass = (Class<T>) field.getType(); Class containingClass = field.getDeclaringClass(); // Verify that the field has an @Intrinsic annotation: Intrinsic intrinsicAnnotation = field.getAnnotation(Intrinsic.class); if (intrinsicAnnotation == null) { throw new IllegalArgumentException("Field \"" + field.getName() + "\" in class " + containingClass.getSimpleName() + " does not have an @Intrinsic annotation"); } // Verify the the field is of a type derived from Object: if (!Object.class.isAssignableFrom(objectClass)) { throw new IllegalArgumentException("@Intrinsic annotations cannot be applied to primitive types"); } PrimitiveArrayModel primitiveArrayModel = getPrimitiveArrayModel(field, intrinsicAnnotation); StructuredArrayModel structuredArrayModel = getStructuredArrayModel(field, intrinsicAnnotation); sanityCheckAnnotation(intrinsicAnnotation, objectClass); IntrinsicObjectModel<T> objectModel = new IntrinsicObjectModel<T>( lookup, field, primitiveArrayModel, structuredArrayModel ); return objectModel; } private static long getLengthFromAnnotation(Intrinsic intrinsicAnnotation, Class objectClass) { long length = intrinsicAnnotation.length(); if (length == Intrinsic.NO_LENGTH) { throw new IllegalArgumentException("length not specified. @Intrinsic annotations of " + objectClass.getSimpleName() + " must specify length (via length = ...)"); } if (length < 0) { throw new IllegalArgumentException("specified length must be positive."); } return length; } private static <T extends AbstractPrimitiveArray> PrimitiveArrayModel<T> getPrimitiveArrayModel( Field field, Intrinsic intrinsicAnnotation) { if (!(AbstractPrimitiveArray.class.isAssignableFrom(field.getType()))) { return null; } @SuppressWarnings("unchecked") Class<T> objectClass = (Class<T>) field.getType(); long length = getLengthFromAnnotation(intrinsicAnnotation, objectClass); return new PrimitiveArrayModel<T>(objectClass, length); } private static <T, S extends StructuredArray<T>> StructuredArrayModel<S, T> getStructuredArrayModel(Field field, Intrinsic intrinsicAnnotation) { if (!StructuredArray.class.isAssignableFrom(field.getType())) { return null; } @SuppressWarnings("unchecked") Class<S> objectClass = (Class<S>) field.getType(); long length = getLengthFromAnnotation(intrinsicAnnotation, objectClass); Class<T> elementClass = deriveElementClass(field, intrinsicAnnotation); return new StructuredArrayModel<S, T>(objectClass, elementClass, length){}; } private static <T> Class<T> deriveElementClass(Field field, Intrinsic intrinsicAnnotation) { @SuppressWarnings("unchecked") Class<T> elementClass = intrinsicAnnotation.elementClass(); Class objectClass = field.getType(); if (!Object.class.isAssignableFrom(elementClass)) { throw new IllegalArgumentException("specified elementClass cannot be a primitive type"); } // Try to infer elementClass from field: Type structuredArrayGenericType; if (objectClass.equals(StructuredArray.class)) { // objectClass is StructuredArray, see if we can infer the generic type: structuredArrayGenericType = field.getGenericType(); } else { // objectClass is a subclass of StructuredArray, see if it establishes a concrete type: Class directSubClass = objectClass; while (!directSubClass.getSuperclass().equals(StructuredArray.class)) { directSubClass = directSubClass.getSuperclass(); } structuredArrayGenericType = directSubClass.getGenericSuperclass(); } try { ParameterizedType p = (ParameterizedType) structuredArrayGenericType; Type t = p.getActualTypeArguments()[0]; if (t instanceof Class) { @SuppressWarnings("unchecked") Class<T> inferredElementClass = (Class<T>) t; if ((elementClass != Intrinsic.NO_DECLARED_CLASS.class) && (elementClass != inferredElementClass)) { throw new IllegalArgumentException("Specified @Intrinsic elementClass (" + elementClass.getName() + ") does not match inferred element class (" + inferredElementClass.getName() + ") for field \"" + field.getName() + "\" (of type " + field.getGenericType() + ") in " + field.getDeclaringClass()); } elementClass = inferredElementClass; } } catch (ClassCastException ex) { // structuredArrayGenericType was not a ParameterizedType. Nothing to see here. Move along. } if (elementClass == Intrinsic.NO_DECLARED_CLASS.class) { throw new IllegalArgumentException( "elementClass not specified. @Intrinsic annotations of " + objectClass.getSimpleName() + " must specify elementClass (via elementClass = ...)"); } return elementClass; } private static <T> void sanityCheckAnnotation(Intrinsic intrinsicAnnotation, Class<T> objectClass) { boolean isPrimitiveArray = AbstractPrimitiveArray.class.isAssignableFrom(objectClass); boolean isStructuredArray = StructuredArray.class.isAssignableFrom(objectClass); if (!isPrimitiveArray && !isStructuredArray) { if (intrinsicAnnotation.length() != Intrinsic.NO_LENGTH) { throw new IllegalArgumentException( "@Intrinsic annotations can only specify length for array types"); } } if (!isStructuredArray) { if (intrinsicAnnotation.elementClass() != Intrinsic.NO_DECLARED_CLASS.class) { throw new IllegalArgumentException( "@Intrinsic annotations can only specify elementClass for StructuredArray types"); } } } }