001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.classfile;
020
021import java.io.DataInput;
022import java.io.IOException;
023import java.util.Objects;
024
025import org.apache.bcel.generic.Type;
026import org.apache.bcel.util.BCELComparator;
027
028/**
029 * This class represents the method info structure, that is, the representation for a method in the class. See JVM
030 * specification for details. A method has access flags, a name, a signature and a number of attributes.
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<Method> bcelComparator = new BCELComparator<Method>() {
042
043        @Override
044        public boolean equals(final Method a, final Method b) {
045            return a == b || a != null && b != null && Objects.equals(a.getName(), b.getName()) && Objects.equals(a.getSignature(), b.getSignature());
046        }
047
048        @Override
049        public int hashCode(final Method o) {
050            return o != null ? Objects.hash(o.getSignature(), o.getName()) : 0;
051        }
052    };
053
054    /**
055     * Gets the comparison strategy object.
056     *
057     * @return Comparison strategy object.
058     */
059    public static BCELComparator<Method> getComparator() {
060        return bcelComparator;
061    }
062
063    /**
064     * Sets the comparison strategy object.
065     *
066     * @param comparator Comparison strategy object.
067     */
068    public static void setComparator(final BCELComparator<Method> comparator) {
069        bcelComparator = comparator;
070    }
071
072    /** Annotations defined on the parameters of a method. */
073    private ParameterAnnotationEntry[] parameterAnnotationEntries;
074
075    /**
076     * Empty constructor, all attributes have to be defined via 'setXXX' methods. Use at your own risk.
077     */
078    public Method() {
079    }
080
081    /**
082     * Constructs a Method from a data input using standard Code headers.
083     *
084     * @param file Input stream.
085     * @param constantPool The constant pool.
086     * @throws IOException Thrown if an I/O error occurs.
087     * @throws ClassFormatException Thrown if the method or one of its attributes is malformed.
088     */
089    Method(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
090        this(file, constantPool, false);
091    }
092
093    /**
094     * Constructs a Method from a data input using the selected Code header layout.
095     *
096     * @param file Input stream.
097     * @param constantPool The constant pool.
098     * @param compactCodeHeader Whether to read any Code attributes encountered with compact four-byte headers ({@code true})
099     *        or standard eight-byte headers ({@code false}).
100     * @throws IOException Thrown if an I/O error occurs.
101     * @throws ClassFormatException Thrown if the method or one of its attributes is malformed.
102     * @see Attribute#readAttribute(DataInput, ConstantPool, boolean)
103     */
104    Method(final DataInput file, final ConstantPool constantPool, final boolean compactCodeHeader) throws IOException {
105        super(file, constantPool, compactCodeHeader);
106    }
107
108    /**
109     * Constructs a Method.
110     *
111     * @param accessFlags Access rights of method.
112     * @param nameIndex Points to method name in constant pool.
113     * @param signatureIndex Points to encoded signature.
114     * @param attributes Collection of attributes.
115     * @param constantPool Array of constants.
116     */
117    public Method(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
118        super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
119    }
120
121    /**
122     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
123     * physical copy.
124     *
125     * @param c Source to copy.
126     */
127    public Method(final Method c) {
128        super(c);
129    }
130
131    /**
132     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
133     * That is, the hierarchy of methods, fields, attributes, and other elements forms a tree of objects.
134     *
135     * @param v Visitor object.
136     */
137    @Override
138    public void accept(final Visitor v) {
139        v.visitMethod(this);
140    }
141
142    /**
143     * Creates a deep copy of this method.
144     *
145     * @param constantPool The constant pool.
146     * @return deep copy of this method.
147     */
148    public Method copy(final ConstantPool constantPool) {
149        return (Method) copy_(constantPool);
150    }
151
152    /**
153     * Return value as defined by given BCELComparator strategy. By default two method objects are said to be equal when
154     * their names and signatures are equal.
155     *
156     * @see Object#equals(Object)
157     */
158    @Override
159    public boolean equals(final Object obj) {
160        return obj instanceof Method && bcelComparator.equals(this, (Method) obj);
161    }
162
163    /**
164     * Gets an array of method argument types.
165     *
166     * @return array of method argument types.
167     */
168    public Type[] getArgumentTypes() {
169        return Type.getArgumentTypes(getSignature());
170    }
171
172    /**
173     * Gets the Code attribute of the method, if any.
174     *
175     * @return Code attribute of method, if any.
176     */
177    public Code getCode() {
178        for (final Attribute attribute : super.getAttributes()) {
179            if (attribute instanceof Code) {
180                return (Code) attribute;
181            }
182        }
183        return null;
184    }
185
186    /**
187     * Gets the ExceptionTable attribute of the method, if any.
188     *
189     * @return ExceptionTable attribute of method, if any, that is, list all exceptions the method may throw not exception handlers.
190     */
191    public ExceptionTable getExceptionTable() {
192        for (final Attribute attribute : super.getAttributes()) {
193            if (attribute instanceof ExceptionTable) {
194                return (ExceptionTable) attribute;
195            }
196        }
197        return null;
198    }
199
200    /**
201     * Gets the LineNumberTable of the code attribute, if any.
202     *
203     * @return LineNumberTable of code attribute if any, for example the call is forwarded to the Code attribute.
204     */
205    public LineNumberTable getLineNumberTable() {
206        final Code code = getCode();
207        if (code == null) {
208            return null;
209        }
210        return code.getLineNumberTable();
211    }
212
213    /**
214     * Gets the LocalVariableTable of the code attribute, if any.
215     *
216     * @return LocalVariableTable of code attribute if any, for example the call is forwarded to the Code attribute.
217     */
218    public LocalVariableTable getLocalVariableTable() {
219        final Code code = getCode();
220        if (code == null) {
221            return null;
222        }
223        return code.getLocalVariableTable();
224    }
225
226    /**
227     * Gets the local variable type table attribute {@link LocalVariableTypeTable}.
228     *
229     * @return LocalVariableTypeTable of code attribute if any, for example the call is forwarded to the Code attribute.
230     * @since 6.10.0
231     */
232    public LocalVariableTypeTable getLocalVariableTypeTable() {
233        final Code code = getCode();
234        if (code == null) {
235            return null;
236        }
237        return code.getLocalVariableTypeTable();
238    }
239
240    /**
241     * Gets the annotations on the parameters of a method.
242     *
243     * @return Annotations on the parameters of a method.
244     * @since 6.0
245     */
246    public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
247        if (parameterAnnotationEntries == null) {
248            parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
249        }
250        return parameterAnnotationEntries;
251    }
252
253    /**
254     * Gets return type of method.
255     *
256     * @return return type of method.
257     */
258    public Type getReturnType() {
259        return Type.getReturnType(getSignature());
260    }
261
262    /**
263     * Return value as defined by given BCELComparator strategy. By default return the hash code of the method's name XOR
264     * signature.
265     *
266     * @see Object#hashCode()
267     */
268    @Override
269    public int hashCode() {
270        return bcelComparator.hashCode(this);
271    }
272
273    /**
274     * Return string representation close to declaration format, 'public static void main(String[] args) throws
275     * IOException', for example.
276     *
277     * @return String representation of the method.
278     */
279    @Override
280    public String toString() {
281        final String access = Utility.accessToString(super.getAccessFlags());
282        // Get name and signature from constant pool
283        ConstantUtf8 c = super.getConstantPool().getConstantUtf8(super.getSignatureIndex());
284        String signature = c.getBytes();
285        c = super.getConstantPool().getConstantUtf8(super.getNameIndex());
286        final String name = c.getBytes();
287        signature = Utility.methodSignatureToString(signature, name, access, true, getLocalVariableTable());
288        final StringBuilder buf = new StringBuilder(signature);
289        for (final Attribute attribute : super.getAttributes()) {
290            if (!(attribute instanceof Code || attribute instanceof ExceptionTable)) {
291                buf.append(" [").append(attribute).append("]");
292            }
293        }
294        final ExceptionTable e = getExceptionTable();
295        if (e != null) {
296            final String str = e.toString();
297            if (!str.isEmpty()) {
298                buf.append("\n\t\tthrows ").append(str);
299            }
300        }
301        return buf.toString();
302    }
303}
304