    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 *
    017 */
    018package org.apache.bcel.classfile;
    019
    020import java.io.DataInput;
    021import java.io.IOException;
    022import java.util.Objects;
    023
    024import org.apache.bcel.generic.Type;
    025import org.apache.bcel.util.BCELComparator;
    026
    027/**
    028 * This class represents the method info structure, i.e., the representation for a method in the class. See JVM
    029 * specification for details. A method has access flags, a name, a signature and a number of attributes.
    030 *
    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 bcelComparator = new BCELComparator() {
    042
    043        @Override
    044        public boolean equals(final Object o1, final Object o2) {
    045            final Method THIS = (Method) o1;
    046            final Method THAT = (Method) o2;
    047            return Objects.equals(THIS.getName(), THAT.getName()) && Objects.equals(THIS.getSignature(), THAT.getSignature());
    048        }
    049
    050        @Override
    051        public int hashCode(final Object o) {
    052            final Method THIS = (Method) o;
    053            return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
    054        }
    055    };
    056
    057    /**
    058     * Empty array.
    059     */
    060    static final Method[] EMPTY_METHOD_ARRAY = {};
    061
    062    /**
    063     * @return Comparison strategy object
    064     */
    065    public static BCELComparator getComparator() {
    066        return bcelComparator;
    067    }
    068
    069    /**
    070     * @param comparator Comparison strategy object
    071     */
    072    public static void setComparator(final BCELComparator comparator) {
    073        bcelComparator = comparator;
    074    }
    075
    076    // annotations defined on the parameters of a method
    077    private ParameterAnnotationEntry[] parameterAnnotationEntries;
    078
    079    /**
    080     * Empty constructor, all attributes have to be defined via `setXXX' methods. Use at your own risk.
    081     */
    082    public Method() {
    083    }
    084
    085    /**
    086     * Construct object from file stream.
    087     *
    088     * @param file Input stream
    089     * @throws IOException if an I/O error occurs.
    090     * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file
    091     */
    092    Method(final DataInput file, final ConstantPool constant_pool) throws IOException, ClassFormatException {
    093        super(file, constant_pool);
    094    }
    095
    096    /**
    097     * @param access_flags Access rights of method
    098     * @param name_index Points to field name in constant pool
    099     * @param signature_index Points to encoded signature
    100     * @param attributes Collection of attributes
    101     * @param constant_pool Array of constants
    102     */
    103    public Method(final int access_flags, final int name_index, final int signature_index, final Attribute[] attributes, final ConstantPool constant_pool) {
    104        super(access_flags, name_index, signature_index, attributes, constant_pool);
    105    }
    106
    107    /**
    108     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
    109     * physical 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}