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}