1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  3  *
  4  * This code is free software; you can redistribute it and/or modify it
  5  * under the terms of the GNU General Public License version 2 only, as
  6  * published by the Free Software Foundation.  Oracle designates this
  7  * particular file as subject to the "Classpath" exception as provided
  8  * by Oracle in the LICENSE file that accompanied this code.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  */
 24 
 25 /*
 26  * This file is available under and governed by the GNU General Public
 27  * License version 2 only, as published by the Free Software Foundation.
 28  * However, the following notice accompanied the original version of this
 29  * file:
 30  *
 31  * ASM: a very small and fast Java bytecode manipulation framework
 32  * Copyright (c) 2000-2011 INRIA, France Telecom
 33  * All rights reserved.
 34  *
 35  * Redistribution and use in source and binary forms, with or without
 36  * modification, are permitted provided that the following conditions
 37  * are met:
 38  * 1. Redistributions of source code must retain the above copyright
 39  *    notice, this list of conditions and the following disclaimer.
 40  * 2. Redistributions in binary form must reproduce the above copyright
 41  *    notice, this list of conditions and the following disclaimer in the
 42  *    documentation and/or other materials provided with the distribution.
 43  * 3. Neither the name of the copyright holders nor the names of its
 44  *    contributors may be used to endorse or promote products derived from
 45  *    this software without specific prior written permission.
 46  *
 47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 57  * THE POSSIBILITY OF SUCH DAMAGE.
 58  */
 59 package jdk.internal.org.objectweb.asm.commons;
 60 
 61 import java.util.HashMap;
 62 import java.util.Map;
 63 import jdk.internal.org.objectweb.asm.Type;
 64 
 65 /**
 66  * A named method descriptor.
 67  *
 68  * @author Juozas Baliuka
 69  * @author Chris Nokleberg
 70  * @author Eric Bruneton
 71  */
 72 public class Method {
 73 
 74     /** The method name. */
 75     private final String name;
 76 
 77     /** The method descriptor. */
 78     private final String descriptor;
 79 
 80     /** The descriptors of the primitive Java types (plus void). */
 81     private static final Map<String, String> PRIMITIVE_TYPE_DESCRIPTORS;
 82 
 83     static {
 84         HashMap<String, String> descriptors = new HashMap<>();
 85         descriptors.put("void", "V");
 86         descriptors.put("byte", "B");
 87         descriptors.put("char", "C");
 88         descriptors.put("double", "D");
 89         descriptors.put("float", "F");
 90         descriptors.put("int", "I");
 91         descriptors.put("long", "J");
 92         descriptors.put("short", "S");
 93         descriptors.put("boolean", "Z");
 94         PRIMITIVE_TYPE_DESCRIPTORS = descriptors;
 95     }
 96 
 97     /**
 98       * Constructs a new {@link Method}.
 99       *
100       * @param name the method's name.
101       * @param descriptor the method's descriptor.
102       */
103     public Method(final String name, final String descriptor) {
104         this.name = name;
105         this.descriptor = descriptor;
106     }
107 
108     /**
109       * Constructs a new {@link Method}.
110       *
111       * @param name the method's name.
112       * @param returnType the method's return type.
113       * @param argumentTypes the method's argument types.
114       */
115     public Method(final String name, final Type returnType, final Type[] argumentTypes) {
116         this(name, Type.getMethodDescriptor(returnType, argumentTypes));
117     }
118 
119     /**
120       * Creates a new {@link Method}.
121       *
122       * @param method a java.lang.reflect method descriptor
123       * @return a {@link Method} corresponding to the given Java method declaration.
124       */
125     public static Method getMethod(final java.lang.reflect.Method method) {
126         return new Method(method.getName(), Type.getMethodDescriptor(method));
127     }
128 
129     /**
130       * Creates a new {@link Method}.
131       *
132       * @param constructor a java.lang.reflect constructor descriptor
133       * @return a {@link Method} corresponding to the given Java constructor declaration.
134       */
135     public static Method getMethod(final java.lang.reflect.Constructor<?> constructor) {
136         return new Method("<init>", Type.getConstructorDescriptor(constructor));
137     }
138 
139     /**
140       * Returns a {@link Method} corresponding to the given Java method declaration.
141       *
142       * @param method a Java method declaration, without argument names, of the form "returnType name
143       *     (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int",
144       *     "float", "java.util.List", ...). Classes of the java.lang package can be specified by their
145       *     unqualified name; all other classes names must be fully qualified.
146       * @return a {@link Method} corresponding to the given Java method declaration.
147       * @throws IllegalArgumentException if <code>method</code> could not get parsed.
148       */
149     public static Method getMethod(final String method) {
150         return getMethod(method, false);
151     }
152 
153     /**
154       * Returns a {@link Method} corresponding to the given Java method declaration.
155       *
156       * @param method a Java method declaration, without argument names, of the form "returnType name
157       *     (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int",
158       *     "float", "java.util.List", ...). Classes of the java.lang package may be specified by their
159       *     unqualified name, depending on the defaultPackage argument; all other classes names must be
160       *     fully qualified.
161       * @param defaultPackage true if unqualified class names belong to the default package, or false
162       *     if they correspond to java.lang classes. For instance "Object" means "Object" if this
163       *     option is true, or "java.lang.Object" otherwise.
164       * @return a {@link Method} corresponding to the given Java method declaration.
165       * @throws IllegalArgumentException if <code>method</code> could not get parsed.
166       */
167     public static Method getMethod(final String method, final boolean defaultPackage) {
168         final int spaceIndex = method.indexOf(' ');
169         int currentArgumentStartIndex = method.indexOf('(', spaceIndex) + 1;
170         final int endIndex = method.indexOf(')', currentArgumentStartIndex);
171         if (spaceIndex == -1 || currentArgumentStartIndex == 0 || endIndex == -1) {
172             throw new IllegalArgumentException();
173         }
174         final String returnType = method.substring(0, spaceIndex);
175         final String methodName =
176                 method.substring(spaceIndex + 1, currentArgumentStartIndex - 1).trim();
177         StringBuilder stringBuilder = new StringBuilder();
178         stringBuilder.append('(');
179         int currentArgumentEndIndex;
180         do {
181             String argumentDescriptor;
182             currentArgumentEndIndex = method.indexOf(',', currentArgumentStartIndex);
183             if (currentArgumentEndIndex == -1) {
184                 argumentDescriptor =
185                         getDescriptorInternal(
186                                 method.substring(currentArgumentStartIndex, endIndex).trim(), defaultPackage);
187             } else {
188                 argumentDescriptor =
189                         getDescriptorInternal(
190                                 method.substring(currentArgumentStartIndex, currentArgumentEndIndex).trim(),
191                                 defaultPackage);
192                 currentArgumentStartIndex = currentArgumentEndIndex + 1;
193             }
194             stringBuilder.append(argumentDescriptor);
195         } while (currentArgumentEndIndex != -1);
196         stringBuilder.append(')').append(getDescriptorInternal(returnType, defaultPackage));
197         return new Method(methodName, stringBuilder.toString());
198     }
199 
200     /**
201       * Returns the descriptor corresponding to the given type name.
202       *
203       * @param type a Java type name.
204       * @param defaultPackage true if unqualified class names belong to the default package, or false
205       *     if they correspond to java.lang classes. For instance "Object" means "Object" if this
206       *     option is true, or "java.lang.Object" otherwise.
207       * @return the descriptor corresponding to the given type name.
208       */
209     private static String getDescriptorInternal(final String type, final boolean defaultPackage) {
210         if ("".equals(type)) {
211             return type;
212         }
213 
214         StringBuilder stringBuilder = new StringBuilder();
215         int arrayBracketsIndex = 0;
216         while ((arrayBracketsIndex = type.indexOf("[]", arrayBracketsIndex) + 1) > 0) {
217             stringBuilder.append('[');
218         }
219 
220         String elementType = type.substring(0, type.length() - stringBuilder.length() * 2);
221         String descriptor = PRIMITIVE_TYPE_DESCRIPTORS.get(elementType);
222         if (descriptor != null) {
223             stringBuilder.append(descriptor);
224         } else {
225             // FIXME: support Q-type
226             stringBuilder.append('L');
227             if (elementType.indexOf('.') < 0) {
228                 if (!defaultPackage) {
229                     stringBuilder.append("java/lang/");
230                 }
231                 stringBuilder.append(elementType);
232             } else {
233                 stringBuilder.append(elementType.replace('.', '/'));
234             }
235             stringBuilder.append(';');
236         }
237         return stringBuilder.toString();
238     }
239 
240     /**
241       * Returns the name of the method described by this object.
242       *
243       * @return the name of the method described by this object.
244       */
245     public String getName() {
246         return name;
247     }
248 
249     /**
250       * Returns the descriptor of the method described by this object.
251       *
252       * @return the descriptor of the method described by this object.
253       */
254     public String getDescriptor() {
255         return descriptor;
256     }
257 
258     /**
259       * Returns the return type of the method described by this object.
260       *
261       * @return the return type of the method described by this object.
262       */
263     public Type getReturnType() {
264         return Type.getReturnType(descriptor);
265     }
266 
267     /**
268       * Returns the argument types of the method described by this object.
269       *
270       * @return the argument types of the method described by this object.
271       */
272     public Type[] getArgumentTypes() {
273         return Type.getArgumentTypes(descriptor);
274     }
275 
276     @Override
277     public String toString() {
278         return name + descriptor;
279     }
280 
281     @Override
282     public boolean equals(final Object other) {
283         if (!(other instanceof Method)) {
284             return false;
285         }
286         Method otherMethod = (Method) other;
287         return name.equals(otherMethod.name) && descriptor.equals(otherMethod.descriptor);
288     }
289 
290     @Override
291     public int hashCode() {
292         return name.hashCode() ^ descriptor.hashCode();
293     }
294 }