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}