Some proposed modifications of CarrierClass

Remi Forax forax at univ-mlv.fr
Sat Nov 6 00:51:52 UTC 2021


Hi Jim,
I've taken a look to the Carrier class.

Here are some small modifications,
- the computation of max locals/max stack for the constructor.
- use hidden class instead of classical class.
- use the Lookup of the hidden class instead of using reflection to get the constructor and the array of components.
- store the constructors and the components in the CarrierClass to avoid to recompute them at each call.

Obviously, feel free to use whatever parts you want.

regards,
Rémi


/*
 * Copyright (c) 2021, 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.MethodHandles.Lookup.ClassOption;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import static org.objectweb.asm.Opcodes.*;

/**
 * This  class is used to create objects that have number and types of
 * components determined at runtime.
 */
public final class Carrier {
    /**
     * Lookup used to define and reference the carrier object classes.
     */
    private static final Lookup LOOKUP;

    /**
     * Maximum number of components in a carrier (based on the maximum
     * number of args to a constructor.)
     */
    private static final int MAX_COMPONENTS = 254;

    /**
     * Maximum number of components in a CarrierClass.
     */
    private static final int MAX_OBJECT_COMPONENTS = 32;

    /*
     * Initialize {@link MethodHandle} constants.
     */
    static {
        Lookup lookup = MethodHandles.lookup();
        LOOKUP = lookup;
        try {
            FLOAT_TO_INT = lookup.findStatic(Float.class, "floatToRawIntBits",
                    MethodType.methodType(int.class, float.class));
            INT_TO_FLOAT = lookup.findStatic(Float.class, "intBitsToFloat",
                    MethodType.methodType(float.class, int.class));
            DOUBLE_TO_LONG = lookup.findStatic(Double.class, "doubleToRawLongBits",
                    MethodType.methodType(long.class, double.class));
            LONG_TO_DOUBLE = lookup.findStatic(Double.class, "longBitsToDouble",
                    MethodType.methodType(double.class, long.class));

            BOOLEAN_TO_INT = lookup.findStatic(Carrier.class, "booleanToInt",
                    MethodType.methodType(int.class, boolean.class));
            INT_TO_BOOLEAN = lookup.findStatic(Carrier.class, "intToBoolean",
                    MethodType.methodType(boolean.class, int.class));
            BYTE_TO_INT = lookup.findStatic(Carrier.class, "byteToInt",
                    MethodType.methodType(int.class, byte.class));
            INT_TO_BYTE = lookup.findStatic(Carrier.class, "intToByte",
                    MethodType.methodType(byte.class, int.class));
            SHORT_TO_INT = lookup.findStatic(Carrier.class, "shortToInt",
                    MethodType.methodType(int.class, short.class));
            INT_TO_SHORT = lookup.findStatic(Carrier.class, "intToShort",
                    MethodType.methodType(short.class, int.class));
            CHAR_TO_INT = lookup.findStatic(Carrier.class, "charToInt",
                    MethodType.methodType(int.class, char.class));
            INT_TO_CHAR = lookup.findStatic(Carrier.class, "intToChar",
                    MethodType.methodType(char.class, int.class));
        } catch (NoSuchMethodException | IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }

    /*
     * Primitive 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;
    private static final MethodHandle BOOLEAN_TO_INT;
    private static final MethodHandle INT_TO_BOOLEAN;
    private static final MethodHandle BYTE_TO_INT;
    private static final MethodHandle INT_TO_BYTE;
    private static final MethodHandle SHORT_TO_INT;
    private static final MethodHandle INT_TO_SHORT;
    private static final MethodHandle CHAR_TO_INT;
    private static final MethodHandle INT_TO_CHAR;

    private static int booleanToInt(boolean b) {
        return b ? 1 : 0;
    }

    private static boolean intToBoolean(int i) {
        return i != 0;
    }

    private static int byteToInt(byte b) {
        return b;
    }

    private static byte intToByte(int i) {
        return (byte)i;
    }

    private static int shortToInt(short s) {
        return s;
    }

    private static short intToShort(int i) {
        return (short)i;
    }

    private static int charToInt(char c) {
        return c;
    }

    private static char intToChar(int i) {
        return (char)i;
    }

    /**
     * Object signature descriptor.
     */
    private static final String OBJECT_DESCRIPTOR =
            Type.getDescriptor(Object.class);

    /**
     * int signature descriptor.
     */
    private static final String INT_DESCRIPTOR =
            Type.getDescriptor(int.class);

    /**
     * long signature descriptor.
     */
    private static final String LONG_DESCRIPTOR =
            Type.getDescriptor(long.class);

    /**
     * Shape of carrier based on counts of each of the three fundamental data
     * types.
     */
    private record CarrierShape(int objectCount, int intCount, int longCount) {
        /**
         * Total number of slots used in a {@link CarrierClass} instance.
         *
         * @return number of slots used
         */
        int slotCount() {
            return objectCount + intCount + longCount * 2;
        }

        /**
         * Returns index of first object component.
         *
         * @return index of first object component
         */
        int objectOffset() {
            return 0;
        }

        /**
         * Returns index of first int component.
         *
         * @return index of first int component
         */
        int intOffset() {
            return objectCount;
        }


        /**
         * Returns index of first long component.
         *
         * @return index of first long component
         */
        int longOffset() {
            return objectCount + intCount;
        }

        /**
         * Returns the constructor method type.
         *
         * @return the constructor method type.
         */
        MethodType constructorMethodType() {
            Class<?>[] array = new Class<?>[objectCount + intCount + longCount];
            int arg = 0;
            for(int i = 0; i < objectCount; i++) {
                array[arg++] = Object.class;
            }
            for(int i = 0; i < intCount; i++) {
                array[arg++] = int.class;
            }
            for(int i = 0; i < longCount; i++) {
                array[arg++] = long.class;
            }
          return MethodType.methodType(void.class, array);
        }
    }

    /**
     * Factory for array based carrier. Array wrapped in object to provide
     * immutability.
     */
    static class CarrierArrayFactory {
        /**
         * Constructor
         *
         * @param ptypes types of carrier constructor arguments
         *
         * @return {@link MethodHandle} to generic carrier constructor.
         */
        static MethodHandle constructor(Class<?>[] ptypes) {
            MethodType methodType = MethodType.methodType(Object.class, ptypes);
            MethodHandle collector = MethodHandles.identity(Object[].class)
                    .withVarargs(true);

            return collector.asType(methodType);
        }

        /**
         * Return an array of carrier component getters, aligning with types in
         * {@code ptypes}.
         *
         * @param ptypes  types of carrier constructor arguments
         *
         * @return array of carrier getters
         */
        static MethodHandle[] components(Class<?>[] ptypes) {
            int length = ptypes.length;
            MethodHandle[] getters = new MethodHandle[length];

            for (int i = 0; i < length; i++) {
                getters[i] = component(ptypes, i);
            }

            return getters;
        }

        /**
         * Return a carrier getter for component {@code i}.
         *
         * @param ptypes  types of carrier constructor arguments
         * @param i       index of parameter to get
         *
         * @return carrier component {@code i} getter {@link MethodHandle}
         */
        static MethodHandle component(Class<?>[] ptypes, int i) {
            MethodType methodType =
                    MethodType.methodType(ptypes[i], Object.class);
            MethodHandle getter =
                    MethodHandles.arrayElementGetter(Object[].class);

            return MethodHandles.insertArguments(
                    getter, 1, i).asType(methodType);
        }
    }

    /**
     * Factory for object based carrier.
     */
    static class CarrierObjectFactory {
        /**
         * Define the hidden class Lookup object
         *
         * @param bytes  class content
         *
         * @return the Lookup object of the hidden class
         */
        static Lookup defineHiddenClass(byte[] bytes) {
            try {
                return LOOKUP.defineHiddenClass(bytes, false, ClassOption.STRONG);
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            }
        }

        /**
         * Generate the name of an object component.
         *
         * @param index field/component index
         *
         * @return name of object component
         */
        private static String objectFieldName(int index) {
            return "o" + index;
        }

        /**
         * Generate the name of an int component.
         *
         * @param index field/component index
         *
         * @return name of int component
         */
        private static String intFieldName(int index) {
            return "i" + index;
        }

        /**
         * Generate the name of a long component.
         *
         * @param index field/component index
         *
         * @return name of long component
         */
        private static String longFieldName(int index) {
            return "l" + index;
        }

        /**
         * Generate the full name of a carrier class based on shape.
         *
         * @param carrierShape  shape of carrier
         *
         * @return name of a carrier class
         */
        static String carrierClassName(CarrierShape carrierShape) {
            String packageName = Carrier.class.getPackageName().replace('.', '/');
            String className = "Carrier$" +
                    objectFieldName(carrierShape.objectCount) +
                    intFieldName(carrierShape.intCount) +
                    longFieldName(carrierShape.longCount);

            return packageName.isEmpty() ? className :
                    packageName + "/" + className;
        }

        /**
         * Construct a new object carrier class based on shape.
         *
         * @param carrierShape  shape of carrier
         *
         * @return a {@link CarrierClass} object containing constructor and
         *         component getters.
         */
        static CarrierClass newCarrierClass(CarrierShape carrierShape) {
            String carrierClassName = carrierClassName(carrierShape);

            StringBuilder initDescriptor = new StringBuilder("(");

            ClassWriter cw = new ClassWriter(0);
            cw.visit(V17, ACC_PUBLIC | ACC_FINAL, carrierClassName,
                    null, "java/lang/Object", null);
            int fieldFlags = ACC_PUBLIC | ACC_FINAL;

            for (int i = 0; i < carrierShape.objectCount; i++) {
                cw.visitField(fieldFlags, objectFieldName(i), OBJECT_DESCRIPTOR,
                        null, null);
                initDescriptor.append(OBJECT_DESCRIPTOR);
            }

            for (int i = 0; i < carrierShape.intCount; i++) {
                cw.visitField(fieldFlags, intFieldName(i), INT_DESCRIPTOR,
                        null, null);
                initDescriptor.append(INT_DESCRIPTOR);
            }

            for (int i = 0; i < carrierShape.longCount; i++) {
                cw.visitField(fieldFlags, longFieldName(i), LONG_DESCRIPTOR,
                        null, null);
                initDescriptor.append(LONG_DESCRIPTOR);
            }

            initDescriptor.append(")V");

            {
                MethodVisitor init = cw.visitMethod(ACC_PUBLIC,
                    "<init>", initDescriptor.toString(), null, null);
                init.visitVarInsn(ALOAD, 0);
                init.visitMethodInsn(INVOKESPECIAL,
                    "java/lang/Object", "<init>", "()V", false);

                int arg = 1;
                for (int i = 0; i < carrierShape.objectCount; i++) {
                    init.visitVarInsn(ALOAD, 0);
                    init.visitVarInsn(ALOAD, arg++);
                    init.visitFieldInsn(PUTFIELD, carrierClassName,
                        objectFieldName(i), OBJECT_DESCRIPTOR);
                }

                for (int i = 0; i < carrierShape.intCount; i++) {
                    init.visitVarInsn(ALOAD, 0);
                    init.visitVarInsn(ILOAD, arg++);
                    init.visitFieldInsn(PUTFIELD, carrierClassName,
                        intFieldName(i), INT_DESCRIPTOR);
                }

                for (int i = 0; i < carrierShape.longCount; i++) {
                    init.visitVarInsn(ALOAD, 0);
                    init.visitVarInsn(LLOAD, arg);
                    arg += 2;
                    init.visitFieldInsn(PUTFIELD, carrierClassName,
                        longFieldName(i), LONG_DESCRIPTOR);
                }

                init.visitInsn(RETURN);
                init.visitMaxs(
                    carrierShape.longCount != 0? 3: ((carrierShape.objectCount != 0 || carrierShape.intCount != 0)? 2: 1)  ,
                    1 /* this */ + carrierShape.slotCount());
                init.visitEnd();
            }

            cw.visitEnd();
            byte[] bytes = cw.toByteArray();

            Lookup hiddenClassLookup = defineHiddenClass(bytes);
            Class<?> hiddenClass = hiddenClassLookup.lookupClass();
            MethodType constructorMethodType = carrierShape.constructorMethodType();
            MethodHandle constructor;
            MethodHandle[] components;
            try {
                constructor = hiddenClassLookup.findConstructor(hiddenClass, constructorMethodType);

                components = new MethodHandle[constructorMethodType.parameterCount()];
                int arg = 0;
                for(int i = 0; i < carrierShape.objectCount; i++) {
                    components[arg++] = hiddenClassLookup.findGetter(hiddenClass,
                        CarrierObjectFactory.objectFieldName(i), Object.class);
                }
                for(int i = 0; i < carrierShape.intCount; i++) {
                    components[arg++] = hiddenClassLookup.findGetter(hiddenClass,
                        CarrierObjectFactory.intFieldName(i), int.class);
                }
                for(int i = 0; i < carrierShape.longCount; i++) {
                    components[arg++] = hiddenClassLookup.findGetter(hiddenClass,
                        CarrierObjectFactory.longFieldName(i), long.class);
                }
            } catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException e) {
                throw new AssertionError(e);
            }

            return new CarrierClass(constructor, components);
        }
    }

    /**
     * Provides constructor and component MethodHandles for a constructed
     * carrier class.
     */
    record CarrierClass(
        /**
         * A raw {@link MethodHandle} for a carrier object constructor.
         * This constructor will only have Object, int and long type arguments.
         *
         */
        MethodHandle constructor,

        /**
         * All the raw {@link MethodHandle MethodHandles} for a carrier
         * component getters. These getters will only return Object, int and
         * long types.
         */
        MethodHandle[] components) {

        /**
         * Create a single raw {@link MethodHandle} for a carrier component
         * getter.
         *
         * @param i  index of component to get
         *
         * @return raw {@link MethodHandle} for the component getter
         */
        MethodHandle component(int i) {
            return components[i];
        }
    }

    /**
     * Cache for all constructed carrier object classes, keyed on class
     * name (i.e., carrier shape.)
     */
    private static final ConcurrentHashMap<String, CarrierClass> carrierCache =
            new ConcurrentHashMap<>();

    /**
     * Constructor
     */
    private Carrier() {
    }

    /**
     * Find or create carrier class for a carrioer shape.
     *
     * @param carrierShape  shape of carrier
     *
     * @return {@link Class<>} of carrier class matching carrier shape
     */
    private static CarrierClass findCarrierClass(CarrierShape carrierShape) {
        String carrierClassName =
                CarrierObjectFactory.carrierClassName(carrierShape);

        return carrierCache.computeIfAbsent(carrierClassName,
                cn -> CarrierObjectFactory.newCarrierClass(carrierShape));
    }

    /**
     * Get the carrier shape based on parameter types.
     *
     * @param ptypes  parameter types
     *
     * @return carrier object shape
     *
     * @throws IllegalArgumentException if number of component slots exceeds maximum
     */
    private static CarrierShape getCarrierShape(Class<?>[] ptypes) {
        int objectCount = 0;
        int intCount = 0;
        int longCount = 0;

        for (Class<?> ptype : ptypes) {
            if (!ptype.isPrimitive()) {
                objectCount++;
            } else if (ptype == long.class || ptype == double.class) {
                longCount++;
            } else {
                intCount++;
            }
        }

        CarrierShape carrierShape = new CarrierShape(objectCount, intCount, longCount);

        if (MAX_COMPONENTS < carrierShape.slotCount()) {
            throw new IllegalArgumentException("Exceeds maximum number of component slots");
        }

        return carrierShape;
    }

    /**
     * Permute a raw constructor {@link MethodHandle} to match the order and
     * types of the parameter types.
     *
     * @param newPTypes     given parameter types
     * @param carrierShape  carrier object shape
     * @param constructor   {@link MethodHandle} to raw carrier object constructor
     *
     * @return {@link MethodHandle} constructor matching parameter types
     */
    private static MethodHandle constructor(Class<?>[] newPTypes,
                                            CarrierShape carrierShape,
                                            MethodHandle constructor) {
        int objectIndex = carrierShape.objectOffset();
        int intIndex = carrierShape.intOffset();
        int longIndex = carrierShape.longOffset();
        int[] reorder = new int[newPTypes.length];
        int index = 0;
        Class<?>[] permutePTypes = new Class<?>[newPTypes.length];

        for (Class<?> ptype : newPTypes) {
            MethodHandle filter = null;
            int from;

            if (!ptype.isPrimitive()) {
                from = objectIndex++;
                ptype = Object.class;
            } else if (ptype == long.class || ptype == double.class) {
                from = longIndex++;
                filter = ptype == double.class ? DOUBLE_TO_LONG : null;
            } else {
                from = intIndex++;

                if (ptype == float.class) {
                    filter = FLOAT_TO_INT;
                } else if (ptype == boolean.class) {
                    filter = BOOLEAN_TO_INT;
                } else if (ptype == byte.class) {
                    filter = BYTE_TO_INT;
                } else if (ptype == short.class) {
                    filter = SHORT_TO_INT;
                } else if (ptype == char.class) {
                    filter = CHAR_TO_INT;
                }
            }

            permutePTypes[index] = ptype;
            reorder[from] = index++;
            constructor = filter == null ? constructor :
                    MethodHandles.filterArguments(constructor, from, filter);
        }

        MethodType permutedMethodType =
                MethodType.methodType(constructor.type().returnType(),
                        permutePTypes);
        MethodType castMethodType =
                MethodType.methodType(Object.class, newPTypes);
        constructor = MethodHandles.permuteArguments(constructor,
                permutedMethodType, reorder);
        constructor = constructor.asType(castMethodType);

        return constructor;
    }

    /**
     * Permute raw component getters to match order and types of the parameter
     * types.
     *
     * @param ptypes        given parameter types
     * @param carrierShape  carrier object shape
     * @param components    raw {@link MethodHandle MethodHandles} to raw
     *                      carrier component getters
     *
     * @return array of components matching parameter types
     */
    private static MethodHandle[] components(Class<?>[] ptypes,
                                             CarrierShape carrierShape,
                                             MethodHandle[] components) {
        MethodHandle[] reorder = new MethodHandle[ptypes.length];
        int objectIndex = 0;
        int intIndex = carrierShape.objectCount;
        int longIndex = carrierShape.objectCount + carrierShape.intCount;
        int index = 0;

        for (Class<?> ptype : ptypes) {
            MethodHandle component;
            MethodHandle filter = null;

            if (!ptype.isPrimitive()) {
                component = components[objectIndex++];
            } else if (ptype == long.class || ptype == double.class) {
                component = components[longIndex++];
                filter = ptype == double.class ? LONG_TO_DOUBLE : null;
            } else {
                component = components[intIndex++];

                if (ptype == float.class) {
                    filter = INT_TO_FLOAT;
                } else if (ptype == boolean.class) {
                    filter = INT_TO_BOOLEAN;
                } else if (ptype == byte.class) {
                    filter = INT_TO_BYTE;
                } else if (ptype == short.class) {
                    filter = INT_TO_SHORT;
                } else if (ptype == char.class) {
                    filter = INT_TO_CHAR;
                }
            }

            component = filter == null ? component :
                    MethodHandles.filterReturnValue(component, filter);
            MethodType methodType = MethodType.methodType(ptype, Object.class);
            reorder[index++] = component.asType(methodType);
        }

        return reorder;
    }

    /**
     * Returns a carrier component getter {@link MethodHandle} for the
     * component {@code i}.
     *
     * @param ptypes        given parameter types
     * @param carrierShape  shape of the carrier object
     * @param carrierClass  carrier object class
     * @param i             index to the component
     *
     * @return carrier component getter {@link MethodHandle}
     *
     * @throws IllegalArgumentException if number of component slots exceeds maximum
     */
    private static MethodHandle component(Class<?>[] ptypes,
                                          CarrierShape carrierShape,
                                          CarrierClass carrierClass,
                                          int i) {
        CarrierShape componentShape = getCarrierShape(Arrays.copyOf(ptypes, i));
        Class<?> ptype = ptypes[i];
        int index;
        MethodHandle filter = null;

        if (!ptype.isPrimitive()) {
            index = carrierShape.objectOffset() + componentShape.objectCount();
        } else if (ptype == long.class || ptype == double.class) {
            index = carrierShape.longOffset() + componentShape.longCount();
            filter = ptype == double.class ? LONG_TO_DOUBLE : null;
        } else {
            index = carrierShape.intOffset() + componentShape.intCount();

            if (ptype == float.class) {
                filter = INT_TO_FLOAT;
            } else if (ptype == boolean.class) {
                filter = INT_TO_BOOLEAN;
            } else if (ptype == byte.class) {
                filter = INT_TO_BYTE;
            } else if (ptype == short.class) {
                filter = INT_TO_SHORT;
            } else if (ptype == char.class) {
                filter = INT_TO_CHAR;
            }
        }

        MethodHandle component = carrierClass.component(index);
        component = filter == null ? component :
                MethodHandles.filterReturnValue(component, filter);

        return component.asType(MethodType.methodType(ptype, Object.class));
    }

    /**
     * Return a constructor {@link MethodHandle} for a carrier with components
     * aligning with the parameter types of the supplied
     * {@link MethodType methodType}.
     *
     * @param methodType {@link MethodType} providing types for the carrier's
     *                   components.
     *
     * @return carrier constructor {@link MethodHandle}
     *
     * @throws NullPointerException is methodType is null
     * @throws IllegalArgumentException if number of component slots exceeds maximum
     */
    public static MethodHandle constructor(MethodType methodType) {
        Objects.requireNonNull(methodType);

        Class<?>[] ptypes = methodType.parameterArray();
        CarrierShape carrierShape = getCarrierShape(ptypes);

        if (carrierShape.slotCount() <= MAX_OBJECT_COMPONENTS) {
            CarrierClass carrierClass = findCarrierClass(carrierShape);

            return constructor(ptypes, carrierShape, carrierClass.constructor());
        } else {
            return CarrierArrayFactory.constructor(ptypes);
        }
    }

    /**
     * Return component getter {@link MethodHandle MethodHandles} for all the
     * carrier's components.
     *
     * @param methodType {@link MethodType} providing types for the carrier's
     *                   components.
     *
     * @return  array of get component {@link MethodHandle MethodHandles}
     *
     * @throws NullPointerException is methodType is null
     * @throws IllegalArgumentException if number of component slots exceeds maximum
     *
     */
    public static MethodHandle[] components(MethodType methodType) {
        Objects.requireNonNull(methodType);
        Class<?>[] ptypes = methodType.parameterArray();
        CarrierShape carrierShape = getCarrierShape(ptypes);
        int slotCount = carrierShape.slotCount();
        MethodHandle[] components;

        if (slotCount <= MAX_OBJECT_COMPONENTS) {
            Carrier.CarrierClass carrierClass = findCarrierClass(carrierShape);
            components = components(ptypes, carrierShape, carrierClass.components());
        } else {
            components = Carrier.CarrierArrayFactory.components(ptypes);
        }

        return components;
    }

    /**
     * Return a component getter {@link MethodHandle} for component {@code i}.
     *
     * @param methodType {@link MethodType} providing types for the carrier's
     *                   components.
     * @param i          component index
     *
     * @return a component getter {@link MethodHandle} for component {@code i}
     *
     * @throws NullPointerException is methodType is null
     * @throws IllegalArgumentException if number of component slots exceeds maximum
     *                                  or if {@code i} is out of bounds
     */
    public static MethodHandle component(MethodType methodType, int i) {
        Objects.requireNonNull(methodType);
        Class<?>[] ptypes = methodType.parameterArray();

        if (i < 0 || i >= ptypes.length) {
            throw new IllegalArgumentException("i is out of bounds for ptypes");
        }

        CarrierShape carrierShape = getCarrierShape(ptypes);

        if (carrierShape.slotCount() <= MAX_OBJECT_COMPONENTS) {
            CarrierClass carrierClass = findCarrierClass(carrierShape);

            return component(ptypes, carrierShape, carrierClass, i);
        } else {
            return CarrierArrayFactory.component(ptypes, i);
        }
    }
}


More information about the amber-dev mailing list