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     * Constructs 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 hash code 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}