    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.bcel.classfile;
    020
    021import java.io.DataInput;
    022import java.io.IOException;
    023import java.util.Objects;
    024
    025import org.apache.bcel.generic.Type;
    026import org.apache.bcel.util.BCELComparator;
    027
    028/**
    029 * This class represents the method info structure, that is, the representation for a method in the class. See JVM
    030 * specification for details. A method has access flags, a name, a signature and a number of attributes.
    031 */
    032public final class Method extends FieldOrMethod {
    033
    034    /**
    035     * Empty array constant.
    036     *
    037     * @since 6.6.0
    038     */
    039    public static final Method[] EMPTY_ARRAY = {};
    040
    041    private static BCELComparator<Method> bcelComparator = new BCELComparator<Method>() {
    042
    043        @Override
    044        public boolean equals(final Method a, final Method b) {
    045            return a == b || a != null && b != null && Objects.equals(a.getName(), b.getName()) && Objects.equals(a.getSignature(), b.getSignature());
    046        }
    047
    048        @Override
    049        public int hashCode(final Method o) {
    050            return o != null ? Objects.hash(o.getSignature(), o.getName()) : 0;
    051        }
    052    };
    053
    054    /**
    055     * Gets the comparison strategy object.
    056     *
    057     * @return Comparison strategy object.
    058     */
    059    public static BCELComparator<Method> getComparator() {
    060        return bcelComparator;
    061    }
    062
    063    /**
    064     * Sets the comparison strategy object.
    065     *
    066     * @param comparator Comparison strategy object.
    067     */
    068    public static void setComparator(final BCELComparator<Method> comparator) {
    069        bcelComparator = comparator;
    070    }
    071
    072    /** Annotations defined on the parameters of a method. */
    073    private ParameterAnnotationEntry[] parameterAnnotationEntries;
    074
    075    /**
    076     * Empty constructor, all attributes have to be defined via 'setXXX' methods. Use at your own risk.
    077     */
    078    public Method() {
    079    }
    080
    081    /**
    082     * Constructs object from file stream.
    083     *
    084     * @param file Input stream.
    085     * @throws IOException if an I/O error occurs.
    086     * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file.
    087     */
    088    Method(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
    089        super(file, constantPool);
    090    }
    091
    092    /**
    093     * Constructs a Method.
    094     *
    095     * @param accessFlags Access rights of method.
    096     * @param nameIndex Points to field name in constant pool.
    097     * @param signatureIndex Points to encoded signature.
    098     * @param attributes Collection of attributes.
    099     * @param constantPool Array of constants.
    100     */
    101    public Method(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
    102        super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
    103    }
    104
    105    /**
    106     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
    107     * physical copy.
    108     *
    109     * @param c Source to copy.
    110     */
    111    public Method(final Method c) {
    112        super(c);
    113    }
    114
    115    /**
    116     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    117     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    118     *
    119     * @param v Visitor object.
    120     */
    121    @Override
    122    public void accept(final Visitor v) {
    123        v.visitMethod(this);
    124    }
    125
    126    /**
    127     * Creates a deep copy of this method.
    128     *
    129     * @param constantPool the constant pool.
    130     * @return deep copy of this method.
    131     */
    132    public Method copy(final ConstantPool constantPool) {
    133        return (Method) copy_(constantPool);
    134    }
    135
    136    /**
    137     * Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when
    138     * their names and signatures are equal.
    139     *
    140     * @see Object#equals(Object)
    141     */
    142    @Override
    143    public boolean equals(final Object obj) {
    144        return obj instanceof Method && bcelComparator.equals(this, (Method) obj);
    145    }
    146
    147    /**
    148     * Gets array of method argument types.
    149     *
    150     * @return array of method argument types.
    151     */
    152    public Type[] getArgumentTypes() {
    153        return Type.getArgumentTypes(getSignature());
    154    }
    155
    156    /**
    157     * Gets Code attribute of method, if any.
    158     *
    159     * @return Code attribute of method, if any.
    160     */
    161    public Code getCode() {
    162        for (final Attribute attribute : super.getAttributes()) {
    163            if (attribute instanceof Code) {
    164                return (Code) attribute;
    165            }
    166        }
    167        return null;
    168    }
    169
    170    /**
    171     * Gets ExceptionTable attribute of method, if any.
    172     *
    173     * @return ExceptionTable attribute of method, if any, that is, list all exceptions the method may throw not exception handlers.
    174     */
    175    public ExceptionTable getExceptionTable() {
    176        for (final Attribute attribute : super.getAttributes()) {
    177            if (attribute instanceof ExceptionTable) {
    178                return (ExceptionTable) attribute;
    179            }
    180        }
    181        return null;
    182    }
    183
    184    /**
    185     * Gets LineNumberTable of code attribute if any.
    186     *
    187     * @return LineNumberTable of code attribute if any, for example the call is forwarded to the Code attribute.
    188     */
    189    public LineNumberTable getLineNumberTable() {
    190        final Code code = getCode();
    191        if (code == null) {
    192            return null;
    193        }
    194        return code.getLineNumberTable();
    195    }
    196
    197    /**
    198     * Gets LocalVariableTable of code attribute if any.
    199     *
    200     * @return LocalVariableTable of code attribute if any, for example the call is forwarded to the Code attribute.
    201     */
    202    public LocalVariableTable getLocalVariableTable() {
    203        final Code code = getCode();
    204        if (code == null) {
    205            return null;
    206        }
    207        return code.getLocalVariableTable();
    208    }
    209
    210    /**
    211     * Gets the local variable type table attribute {@link LocalVariableTypeTable}.
    212     *
    213     * @return LocalVariableTypeTable of code attribute if any, for example the call is forwarded to the Code attribute.
    214     * @since 6.10.0
    215     */
    216    public LocalVariableTypeTable getLocalVariableTypeTable() {
    217        final Code code = getCode();
    218        if (code == null) {
    219            return null;
    220        }
    221        return code.getLocalVariableTypeTable();
    222    }
    223
    224    /**
    225     * Gets Annotations on the parameters of a method.
    226     *
    227     * @return Annotations on the parameters of a method.
    228     * @since 6.0
    229     */
    230    public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
    231        if (parameterAnnotationEntries == null) {
    232            parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
    233        }
    234        return parameterAnnotationEntries;
    235    }
    236
    237    /**
    238     * Gets return type of method.
    239     *
    240     * @return return type of method.
    241     */
    242    public Type getReturnType() {
    243        return Type.getReturnType(getSignature());
    244    }
    245
    246    /**
    247     * Return value as defined by given BCELComparator strategy. By default return the hash code of the method's name XOR
    248     * signature.
    249     *
    250     * @see Object#hashCode()
    251     */
    252    @Override
    253    public int hashCode() {
    254        return bcelComparator.hashCode(this);
    255    }
    256
    257    /**
    258     * Return string representation close to declaration format, 'public static void main(String[] args) throws
    259     * IOException', for example.
    260     *
    261     * @return String representation of the method.
    262     */
    263    @Override
    264    public String toString() {
    265        final String access = Utility.accessToString(super.getAccessFlags());
    266        // Get name and signature from constant pool
    267        ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex());
    268        String signature = c.getBytes();
    269        c = super.getConstantPool().getConstantUtf8(super.getNameIndex());
    270        final String name = c.getBytes();
    271        signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable());
    272        final StringBuilder buf = new StringBuilder(signature);
    273        for (final Attribute attribute : super.getAttributes()) {
    274            if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) {
    275                buf.append(" [").append(attribute).append("]");
    276            }
    277        }
    278        final ExceptionTable e = getExceptionTable();
    279        if (e != null) {
    280            final String str = e.toString();
    281            if (!str.isEmpty()) {
    282                buf.append("\n\t\tthrows ").append(str);
    283            }
    284        }
    285        return buf.toString();
    286    }
    287}
    288