    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, i.e., 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     * @return Comparison strategy object.
    056     */
    057    public static BCELComparator<Method> getComparator() {
    058        return bcelComparator;
    059    }
    060
    061    /**
    062     * @param comparator Comparison strategy object.
    063     */
    064    public static void setComparator(final BCELComparator<Method> comparator) {
    065        bcelComparator = comparator;
    066    }
    067
    068    /** Annotations defined on the parameters of a method. */
    069    private ParameterAnnotationEntry[] parameterAnnotationEntries;
    070
    071    /**
    072     * Empty constructor, all attributes have to be defined via 'setXXX' methods. Use at your own risk.
    073     */
    074    public Method() {
    075    }
    076
    077    /**
    078     * Constructs object from file stream.
    079     *
    080     * @param file Input stream
    081     * @throws IOException if an I/O error occurs.
    082     * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
    083     */
    084    Method(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
    085        super(file, constantPool);
    086    }
    087
    088    /**
    089     * @param accessFlags Access rights of method
    090     * @param nameIndex Points to field name in constant pool
    091     * @param signatureIndex Points to encoded signature
    092     * @param attributes Collection of attributes
    093     * @param constantPool Array of constants
    094     */
    095    public Method(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
    096        super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
    097    }
    098
    099    /**
    100     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
    101     * physical copy.
    102     *
    103     * @param c Source to copy.
    104     */
    105    public Method(final Method c) {
    106        super(c);
    107    }
    108
    109    /**
    110     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    111     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    112     *
    113     * @param v Visitor object
    114     */
    115    @Override
    116    public void accept(final Visitor v) {
    117        v.visitMethod(this);
    118    }
    119
    120    /**
    121     * @return deep copy of this method
    122     */
    123    public Method copy(final ConstantPool constantPool) {
    124        return (Method) copy_(constantPool);
    125    }
    126
    127    /**
    128     * Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when
    129     * their names and signatures are equal.
    130     *
    131     * @see Object#equals(Object)
    132     */
    133    @Override
    134    public boolean equals(final Object obj) {
    135        return obj instanceof Method && bcelComparator.equals(this, (Method) obj);
    136    }
    137
    138    /**
    139     * @return array of method argument types
    140     */
    141    public Type[] getArgumentTypes() {
    142        return Type.getArgumentTypes(getSignature());
    143    }
    144
    145    /**
    146     * @return Code attribute of method, if any
    147     */
    148    public Code getCode() {
    149        for (final Attribute attribute : super.getAttributes()) {
    150            if (attribute instanceof Code) {
    151                return (Code) attribute;
    152            }
    153        }
    154        return null;
    155    }
    156
    157    /**
    158     * @return ExceptionTable attribute of method, if any, i.e., list all exceptions the method may throw not exception
    159     *         handlers!
    160     */
    161    public ExceptionTable getExceptionTable() {
    162        for (final Attribute attribute : super.getAttributes()) {
    163            if (attribute instanceof ExceptionTable) {
    164                return (ExceptionTable) attribute;
    165            }
    166        }
    167        return null;
    168    }
    169
    170    /**
    171     * @return LineNumberTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
    172     */
    173    public LineNumberTable getLineNumberTable() {
    174        final Code code = getCode();
    175        if (code == null) {
    176            return null;
    177        }
    178        return code.getLineNumberTable();
    179    }
    180
    181    /**
    182     * @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code attribute.
    183     */
    184    public LocalVariableTable getLocalVariableTable() {
    185        final Code code = getCode();
    186        if (code == null) {
    187            return null;
    188        }
    189        return code.getLocalVariableTable();
    190    }
    191
    192    /**
    193     * Gets the local variable type table attribute {@link LocalVariableTypeTable}.
    194     * @return LocalVariableTypeTable of code attribute if any, i.e. the call is forwarded to the Code attribute.
    195     * @since 6.10.0
    196     */
    197    public LocalVariableTypeTable getLocalVariableTypeTable() {
    198        final Code code = getCode();
    199        if (code == null) {
    200            return null;
    201        }
    202        return code.getLocalVariableTypeTable();
    203    }
    204
    205    /**
    206     * @return Annotations on the parameters of a method
    207     * @since 6.0
    208     */
    209    public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
    210        if (parameterAnnotationEntries == null) {
    211            parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
    212        }
    213        return parameterAnnotationEntries;
    214    }
    215
    216    /**
    217     * @return return type of method
    218     */
    219    public Type getReturnType() {
    220        return Type.getReturnType(getSignature());
    221    }
    222
    223    /**
    224     * Return value as defined by given BCELComparator strategy. By default return the hash code of the method's name XOR
    225     * signature.
    226     *
    227     * @see Object#hashCode()
    228     */
    229    @Override
    230    public int hashCode() {
    231        return bcelComparator.hashCode(this);
    232    }
    233
    234    /**
    235     * Return string representation close to declaration format, 'public static void main(String[] args) throws
    236     * IOException', for example.
    237     *
    238     * @return String representation of the method.
    239     */
    240    @Override
    241    public String toString() {
    242        final String access = Utility.accessToString(super.getAccessFlags());
    243        // Get name and signature from constant pool
    244        ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex());
    245        String signature = c.getBytes();
    246        c = super.getConstantPool().getConstantUtf8(super.getNameIndex());
    247        final String name = c.getBytes();
    248        signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable());
    249        final StringBuilder buf = new StringBuilder(signature);
    250        for (final Attribute attribute : super.getAttributes()) {
    251            if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) {
    252                buf.append(" [").append(attribute).append("]");
    253            }
    254        }
    255        final ExceptionTable e = getExceptionTable();
    256        if (e != null) {
    257            final String str = e.toString();
    258            if (!str.isEmpty()) {
    259                buf.append("\n\t\tthrows ").append(str);
    260            }
    261        }
    262        return buf.toString();
    263    }
    264}