All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ui4j.bytebuddy.instrumentation.method.ParameterDescription Maven / Gradle / Ivy

The newest version!
package com.ui4j.bytebuddy.instrumentation.method;

import com.ui4j.bytebuddy.instrumentation.ModifierReviewable;
import com.ui4j.bytebuddy.instrumentation.NamedElement;
import com.ui4j.bytebuddy.instrumentation.attribute.annotation.AnnotatedElement;
import com.ui4j.bytebuddy.instrumentation.attribute.annotation.AnnotationList;
import com.ui4j.bytebuddy.instrumentation.method.bytecode.stack.StackSize;
import com.ui4j.bytebuddy.instrumentation.type.TypeDescription;
import com.ui4j.bytebuddy.instrumentation.type.TypeList;
import com.ui4j.bytebuddy.utility.JavaMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Description of the parameter of a Java method or constructor.
 */
public interface ParameterDescription extends AnnotatedElement, NamedElement, ModifierReviewable {

    /**
     * The prefix for names of an unnamed parameter.
     */
    static final String NAME_PREFIX = "arg";

    /**
     * Returns the parameter's type.
     *
     * @return The parameter's type.
     */
    TypeDescription getTypeDescription();

    /**
     * Returns the method that declares this parameter.
     *
     * @return The method that declares this parameter.
     */
    MethodDescription getDeclaringMethod();

    /**
     * Returns this parameter's index.
     *
     * @return The index of this parameter.
     */
    int getIndex();

    /**
     * Checks if this parameter has an explicit name. A parameter without an explicit name is named implicitly by
     * {@code argX} with {@code X} denoting the zero-based index.
     *
     * @return {@code true} if the parameter has an explicit name.
     */
    boolean isNamed();

    /**
     * Checks if this parameter has an explicit modifier. A parameter without a modifier is simply treated as
     * if it had a modifier of zero.
     *
     * @return {@code true} if this parameter defines explicit modifiers.
     */
    boolean hasModifiers();

    /**
     * Returns the offset to the parameter value within the local method variable.
     *
     * @return The offset of this parameter's value.
     */
    int getOffset();

    /**
     * A base implementation of a method parameter description.
     */
    abstract static class AbstractParameterDescription extends AbstractModifierReviewable implements ParameterDescription {

        @Override
        public String getName() {
            return NAME_PREFIX.concat(String.valueOf(getIndex()));
        }

        @Override
        public String getInternalName() {
            return getName();
        }

        @Override
        public String getSourceCodeName() {
            return isNamed()
                    ? getName()
                    : EMPTY_NAME;
        }

        @Override
        public int getModifiers() {
            return EMPTY_MASK;
        }

        @Override
        public int getOffset() {
            TypeList parameterType = getDeclaringMethod().getParameters().asTypeList();
            int offset = getDeclaringMethod().isStatic()
                    ? StackSize.ZERO.getSize()
                    : StackSize.SINGLE.getSize();
            for (int i = 0; i < getIndex(); i++) {
                offset += parameterType.get(i).getStackSize().getSize();
            }
            return offset;
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            } else if (!(other instanceof ParameterDescription)) {
                return false;
            }
            ParameterDescription parameterDescription = (ParameterDescription) other;
            return getDeclaringMethod().equals(parameterDescription.getDeclaringMethod())
                    && getIndex() == parameterDescription.getIndex();
        }

        @Override
        public int hashCode() {
            return getDeclaringMethod().hashCode() ^ getIndex();
        }

        @Override
        public String toString() {
            StringBuilder stringBuilder = new StringBuilder(Modifier.toString(getModifiers()));
            if (getModifiers() != EMPTY_MASK) {
                stringBuilder.append(' ');
            }
            stringBuilder.append(isVarArgs()
                    ? getTypeDescription().getName().replaceFirst("\\[\\]$", "...")
                    : getTypeDescription().getName());
            return stringBuilder.append(' ').append(getName()).toString();
        }
    }

    /**
     * Description of a loaded parameter, represented by a Java 8 {@code java.lang.reflect.Parameter}.
     */
    static class ForLoadedParameter extends AbstractParameterDescription {

        /**
         * Java method representation for the {@code java.lang.reflect.Parameter}'s {@code getType} method.
         */
        protected static final JavaMethod GET_TYPE;

        /**
         * Initializes the {@link com.ui4j.bytebuddy.utility.JavaMethod} instances of this class dependant on
         * whether they are available.
         */
        static {
            JavaMethod getName, getDeclaringExecutable, isNamePresent, getModifiers, getDeclaredAnnotations, getType;
            try {
                Class parameterType = Class.forName("java.lang.reflect.Parameter");
                getName = new JavaMethod.ForLoadedMethod(parameterType.getDeclaredMethod("getName"));
                getDeclaringExecutable = new JavaMethod.ForLoadedMethod(parameterType.getDeclaredMethod("getDeclaringExecutable"));
                isNamePresent = new JavaMethod.ForLoadedMethod(parameterType.getDeclaredMethod("isNamePresent"));
                getModifiers = new JavaMethod.ForLoadedMethod(parameterType.getDeclaredMethod("getModifiers"));
                getDeclaredAnnotations = new JavaMethod.ForLoadedMethod(parameterType.getDeclaredMethod("getDeclaredAnnotations"));
                getType = new JavaMethod.ForLoadedMethod(parameterType.getDeclaredMethod("getType"));
            } catch (Exception ignored) {
                getName = JavaMethod.ForUnavailableMethod.INSTANCE;
                getDeclaringExecutable = JavaMethod.ForUnavailableMethod.INSTANCE;
                isNamePresent = JavaMethod.ForUnavailableMethod.INSTANCE;
                getModifiers = JavaMethod.ForUnavailableMethod.INSTANCE;
                getDeclaredAnnotations = JavaMethod.ForUnavailableMethod.INSTANCE;
                getType = JavaMethod.ForUnavailableMethod.INSTANCE;
            }
            GET_NAME = getName;
            GET_DECLARING_EXECUTABLE = getDeclaringExecutable;
            IS_NAME_PRESENT = isNamePresent;
            GET_MODIFIERS = getModifiers;
            GET_DECLARED_ANNOTATIONS = getDeclaredAnnotations;
            GET_TYPE = getType;
        }

        /**
         * Java method representation for the {@code java.lang.reflect.Parameter}'s {@code getName} method.
         */
        private static final JavaMethod GET_NAME;

        /**
         * Java method representation for the {@code java.lang.reflect.Parameter}'s
         * {@code getDeclaringExecutable} method.
         */
        private static final JavaMethod GET_DECLARING_EXECUTABLE;

        /**
         * Java method representation for the {@code java.lang.reflect.Parameter}'s {@code isNamePresent} method.
         */
        private static final JavaMethod IS_NAME_PRESENT;

        /**
         * Java method representation for the {@code java.lang.reflect.Parameter}'s {@code getModifiers} method.
         */
        private static final JavaMethod GET_MODIFIERS;

        /**
         * Java method representation for the {@code java.lang.reflect.Parameter}'s {@code getDeclaredAnnotations}
         * method.
         */
        private static final JavaMethod GET_DECLARED_ANNOTATIONS;

        /**
         * An instance of {@code java.lang.reflect.Parameter}.
         */
        private final Object parameter;

        /**
         * The parameter's index.
         */
        private final int index;

        /**
         * Creates a representation of a loaded parameter.
         *
         * @param parameter An instance of {@code java.lang.reflect.Parameter}.
         * @param index     The parameter's index.
         */
        protected ForLoadedParameter(Object parameter, int index) {
            this.parameter = parameter;
            this.index = index;
        }

        @Override
        public TypeDescription getTypeDescription() {
            return new TypeDescription.ForLoadedType((Class) GET_TYPE.invoke(parameter));
        }

        @Override
        public MethodDescription getDeclaringMethod() {
            Object executable = GET_DECLARING_EXECUTABLE.invoke(parameter);
            if (executable instanceof Method) {
                return new MethodDescription.ForLoadedMethod((Method) executable);
            } else if (executable instanceof Constructor) {
                return new MethodDescription.ForLoadedConstructor((Constructor) executable);
            } else {
                throw new IllegalStateException("Unknown executable type: " + executable);
            }
        }

        @Override
        public AnnotationList getDeclaredAnnotations() {
            return new AnnotationList.ForLoadedAnnotation((Annotation[]) GET_DECLARED_ANNOTATIONS.invoke(parameter));
        }

        @Override
        public String getName() {
            return (String) GET_NAME.invoke(parameter);
        }

        @Override
        public int getIndex() {
            return index;
        }

        @Override
        public boolean isNamed() {
            return (Boolean) IS_NAME_PRESENT.invoke(parameter);
        }

        @Override
        public int getModifiers() {
            return (Integer) GET_MODIFIERS.invoke(parameter);
        }

        @Override
        public boolean hasModifiers() {
            // Rational: If a parameter is not named despite the information being attached, it is synthetic.
            return isNamed() || getModifiers() != EMPTY_MASK;
        }

        /**
         * Description of a loaded method's parameter on a virtual machine where {@code java.lang.reflect.Parameter}
         * is not available.
         */
        protected static class OfLegacyVmMethod extends AbstractParameterDescription {

            /**
             * The method that declares this parameter.
             */
            private final Method method;

            /**
             * The index of this parameter.
             */
            private final int index;

            /**
             * The type of this parameter.
             */
            private final Class parameterType;

            /**
             * The annotations of this parameter.
             */
            private final Annotation[] parameterAnnotation;

            /**
             * Creates a legacy representation of a method's parameter.
             *
             * @param method              The method that declares this parameter.
             * @param index               The index of this parameter.
             * @param parameterType       The type of this parameter.
             * @param parameterAnnotation The annotations of this parameter.
             */
            protected OfLegacyVmMethod(Method method, int index, Class parameterType, Annotation[] parameterAnnotation) {
                this.method = method;
                this.index = index;
                this.parameterType = parameterType;
                this.parameterAnnotation = parameterAnnotation;
            }

            @Override
            public TypeDescription getTypeDescription() {
                return new TypeDescription.ForLoadedType(parameterType);
            }

            @Override
            public MethodDescription getDeclaringMethod() {
                return new MethodDescription.ForLoadedMethod(method);
            }

            @Override
            public int getIndex() {
                return index;
            }

            @Override
            public boolean isNamed() {
                return false;
            }

            @Override
            public boolean hasModifiers() {
                return false;
            }

            @Override
            public AnnotationList getDeclaredAnnotations() {
                return new AnnotationList.ForLoadedAnnotation(parameterAnnotation);
            }
        }

        /**
         * Description of a loaded constructor's parameter on a virtual machine where {@code java.lang.reflect.Parameter}
         * is not available.
         */
        protected static class OfLegacyVmConstructor extends AbstractParameterDescription {

            /**
             * The method that declares this parameter.
             */
            private final Constructor constructor;

            /**
             * The index of this parameter.
             */
            private final int index;

            /**
             * The type of this parameter.
             */
            private final Class parameterType;

            /**
             * The annotations of this parameter.
             */
            private final Annotation[] parameterAnnotation;

            /**
             * Creates a legacy representation of a method's parameter.
             *
             * @param constructor         The constructor that declares this parameter.
             * @param index               The index of this parameter.
             * @param parameterType       The type of this parameter.
             * @param parameterAnnotation The annotations of this parameter.
             */
            protected OfLegacyVmConstructor(Constructor constructor, int index, Class parameterType, Annotation[] parameterAnnotation) {
                this.constructor = constructor;
                this.index = index;
                this.parameterType = parameterType;
                this.parameterAnnotation = parameterAnnotation;
            }

            @Override
            public TypeDescription getTypeDescription() {
                return new TypeDescription.ForLoadedType(parameterType);
            }

            @Override
            public MethodDescription getDeclaringMethod() {
                return new MethodDescription.ForLoadedConstructor(constructor);
            }

            @Override
            public int getIndex() {
                return index;
            }

            @Override
            public boolean isNamed() {
                return false;
            }

            @Override
            public boolean hasModifiers() {
                return false;
            }

            @Override
            public AnnotationList getDeclaredAnnotations() {
                return new AnnotationList.ForLoadedAnnotation(parameterAnnotation);
            }
        }
    }

    /**
     * A latent description of a parameter that is not attached to a method or constructor.
     */
    static class Latent extends AbstractParameterDescription {

        /**
         * The method that is declaring the parameter.
         */
        private final MethodDescription declaringMethod;

        /**
         * The type of the parameter.
         */
        private final TypeDescription parameterType;

        /**
         * The index of the parameter.
         */
        private final int index;

        /**
         * The parameter's offset in the local method variables array.
         */
        private final int offset;

        /**
         * Creates a latent description of a parameter.
         *
         * @param declaringMethod The method that is declaring the parameter.
         * @param parameterType   The type of the parameter.
         * @param index           The index of the parameter.
         * @param offset          The parameter's offset in the local method variables array.
         */
        public Latent(MethodDescription declaringMethod,
                      TypeDescription parameterType,
                      int index,
                      int offset) {
            this.declaringMethod = declaringMethod;
            this.parameterType = parameterType;
            this.index = index;
            this.offset = offset;
        }

        @Override
        public TypeDescription getTypeDescription() {
            return parameterType;
        }

        @Override
        public MethodDescription getDeclaringMethod() {
            return declaringMethod;
        }

        @Override
        public int getIndex() {
            return index;
        }

        @Override
        public int getOffset() {
            return offset;
        }

        @Override
        public boolean isNamed() {
            return false;
        }

        @Override
        public boolean hasModifiers() {
            return false;
        }

        @Override
        public AnnotationList getDeclaredAnnotations() {
            return new AnnotationList.Empty();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy