    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 bcelComparator = new BCELComparator() {
    040
    041        @Override
    042        public boolean equals(final Object o1, final Object o2) {
    043            final Method THIS = (Method) o1;
    044            final Method THAT = (Method) o2;
    045            return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature());
    046        }
    047
    048        @Override
    049        public int hashCode(final Object o) {
    050            final Method THIS = (Method) o;
    051            return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
    052        }
    053    };
    054
    055    /**
    056     * Empty array.
    057     */
    058    static final Method[] EMPTY_METHOD_ARRAY = {};
    059
    060    /**
    061     * @return Comparison strategy object
    062     */
    063    public static BCELComparator getComparator() {
    064        return bcelComparator;
    065    }
    066
    067    /**
    068     * @param comparator Comparison strategy object
    069     */
    070    public static void setComparator(final BCELComparator comparator) {
    071        bcelComparator = comparator;
    072    }
    073
    074    // annotations defined on the parameters of a method
    075    private ParameterAnnotationEntry[] parameterAnnotationEntries;
    076
    077    /**
    078     * Empty constructor, all attributes have to be defined via 'setXXX' methods. Use at your own risk.
    079     */
    080    public Method() {
    081    }
    082
    083    /**
    084     * Construct object from file stream.
    085     *
    086     * @param file Input stream
    087     * @throws IOException if an I/O error occurs.
    088     * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
    089     */
    090    Method(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
    091        super(file, constantPool);
    092    }
    093
    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     * @return deep copy of this method
    128     */
    129    public Method copy(final ConstantPool constantPool) {
    130        return (Method) copy_(constantPool);
    131    }
    132
    133    /**
    134     * Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when
    135     * their names and signatures are equal.
    136     *
    137     * @see Object#equals(Object)
    138     */
    139    @Override
    140    public boolean equals(final Object obj) {
    141        return bcelComparator.equals(this, obj);
    142    }
    143
    144    /**
    145     * @return array of method argument types
    146     */
    147    public Type[] getArgumentTypes() {
    148        return Type.getArgumentTypes(getSignature());
    149    }
    150
    151    /**
    152     * @return Code attribute of method, if any
    153     */
    154    public Code getCode() {
    155        for (final Attribute attribute : super.getAttributes()) {
    156            if (attribute instanceof Code) {
    157                return (Code) attribute;
    158            }
    159        }
    160        return null;
    161    }
    162
    163    /**
    164     * @return ExceptionTable attribute of method, if any, i.e., list all exceptions the method may throw not exception
    165     *         handlers!
    166     */
    167    public ExceptionTable getExceptionTable() {
    168        for (final Attribute attribute : super.getAttributes()) {
    169            if (attribute instanceof ExceptionTable) {
    170                return (ExceptionTable) attribute;
    171            }
    172        }
    173        return null;
    174    }
    175
    176    /**
    177     * @return LineNumberTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
    178     */
    179    public LineNumberTable getLineNumberTable() {
    180        final Code code = getCode();
    181        if (code == null) {
    182            return null;
    183        }
    184        return code.getLineNumberTable();
    185    }
    186
    187    /**
    188     * @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
    189     */
    190    public LocalVariableTable getLocalVariableTable() {
    191        final Code code = getCode();
    192        if (code == null) {
    193            return null;
    194        }
    195        return code.getLocalVariableTable();
    196    }
    197
    198    /**
    199     * @return Annotations on the parameters of a method
    200     * @since 6.0
    201     */
    202    public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
    203        if (parameterAnnotationEntries == null) {
    204            parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
    205        }
    206        return parameterAnnotationEntries;
    207    }
    208
    209    /**
    210     * @return return type of method
    211     */
    212    public Type getReturnType() {
    213        return Type.getReturnType(getSignature());
    214    }
    215
    216    /**
    217     * Return value as defined by given BCELComparator strategy. By default return the hashcode of the method's name XOR
    218     * signature.
    219     *
    220     * @see Object#hashCode()
    221     */
    222    @Override
    223    public int hashCode() {
    224        return bcelComparator.hashCode(this);
    225    }
    226
    227    /**
    228     * Return string representation close to declaration format, 'public static void main(String[] args) throws
    229     * IOException', e.g.
    230     *
    231     * @return String representation of the method.
    232     */
    233    @Override
    234    public String toString() {
    235        final String access = Utility.accessToString(super.getAccessFlags());
    236        // Get name and signature from constant pool
    237        ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex());
    238        String signature = c.getBytes();
    239        c = super.getConstantPool().getConstantUtf8(super.getNameIndex());
    240        final String name = c.getBytes();
    241        signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable());
    242        final StringBuilder buf = new StringBuilder(signature);
    243        for (final Attribute attribute : super.getAttributes()) {
    244            if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) {
    245                buf.append(" [").append(attribute).append("]");
    246            }
    247        }
    248        final ExceptionTable e = getExceptionTable();
    249        if (e != null) {
    250            final String str = e.toString();
    251            if (!str.isEmpty()) {
    252                buf.append("\n\t\tthrows ").append(str);
    253            }
    254        }
    255        return buf.toString();
    256    }
    257}