    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    public Method(final Method c) {
    110        super(c);
    111    }
    112
    113    /**
    114     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    115     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    116     *
    117     * @param v Visitor object
    118     */
    119    @Override
    120    public void accept(final Visitor v) {
    121        v.visitMethod(this);
    122    }
    123
    124    /**
    125     * @return deep copy of this method
    126     */
    127    public Method copy(final ConstantPool constantPool) {
    128        return (Method) copy_(constantPool);
    129    }
    130
    131    /**
    132     * Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when
    133     * their names and signatures are equal.
    134     *
    135     * @see Object#equals(Object)
    136     */
    137    @Override
    138    public boolean equals(final Object obj) {
    139        return bcelComparator.equals(this, obj);
    140    }
    141
    142    /**
    143     * @return array of method argument types
    144     */
    145    public Type[] getArgumentTypes() {
    146        return Type.getArgumentTypes(getSignature());
    147    }
    148
    149    /**
    150     * @return Code attribute of method, if any
    151     */
    152    public Code getCode() {
    153        for (final Attribute attribute : super.getAttributes()) {
    154            if (attribute instanceof Code) {
    155                return (Code) attribute;
    156            }
    157        }
    158        return null;
    159    }
    160
    161    /**
    162     * @return ExceptionTable attribute of method, if any, i.e., list all exceptions the method may throw not exception
    163     *         handlers!
    164     */
    165    public ExceptionTable getExceptionTable() {
    166        for (final Attribute attribute : super.getAttributes()) {
    167            if (attribute instanceof ExceptionTable) {
    168                return (ExceptionTable) attribute;
    169            }
    170        }
    171        return null;
    172    }
    173
    174    /**
    175     * @return LineNumberTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
    176     */
    177    public LineNumberTable getLineNumberTable() {
    178        final Code code = getCode();
    179        if (code == null) {
    180            return null;
    181        }
    182        return code.getLineNumberTable();
    183    }
    184
    185    /**
    186     * @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code atribute.
    187     */
    188    public LocalVariableTable getLocalVariableTable() {
    189        final Code code = getCode();
    190        if (code == null) {
    191            return null;
    192        }
    193        return code.getLocalVariableTable();
    194    }
    195
    196    /**
    197     * @return Annotations on the parameters of a method
    198     * @since 6.0
    199     */
    200    public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
    201        if (parameterAnnotationEntries == null) {
    202            parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
    203        }
    204        return parameterAnnotationEntries;
    205    }
    206
    207    /**
    208     * @return return type of method
    209     */
    210    public Type getReturnType() {
    211        return Type.getReturnType(getSignature());
    212    }
    213
    214    /**
    215     * Return value as defined by given BCELComparator strategy. By default return the hashcode of the method's name XOR
    216     * signature.
    217     *
    218     * @see Object#hashCode()
    219     */
    220    @Override
    221    public int hashCode() {
    222        return bcelComparator.hashCode(this);
    223    }
    224
    225    /**
    226     * Return string representation close to declaration format, `public static void main(String[] args) throws
    227     * IOException', e.g.
    228     *
    229     * @return String representation of the method.
    230     */
    231    @Override
    232    public String toString() {
    233        final String access = Utility.accessToString(super.getAccessFlags());
    234        // Get name and signature from constant pool
    235        ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex());
    236        String signature = c.getBytes();
    237        c = super.getConstantPool().getConstantUtf8(super.getNameIndex());
    238        final String name = c.getBytes();
    239        signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable());
    240        final StringBuilder buf = new StringBuilder(signature);
    241        for (final Attribute attribute : super.getAttributes()) {
    242            if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) {
    243                buf.append(" [").append(attribute).append("]");
    244            }
    245        }
    246        final ExceptionTable e = getExceptionTable();
    247        if (e != null) {
    248            final String str = e.toString();
    249            if (!str.isEmpty()) {
    250                buf.append("\n\t\tthrows ").append(str);
    251            }
    252        }
    253        return buf.toString();
    254    }
    255}