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