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 
 60 package jdk.internal.org.objectweb.asm.commons;
 61 
 62 import java.util.HashMap;
 63 import java.util.Map;
 64 import jdk.internal.org.objectweb.asm.Type;
 65 
 66 /**
 67  * A named method descriptor.
 68  *
 69  * @author Juozas Baliuka
 70  * @author Chris Nokleberg
 71  * @author Eric Bruneton
 72  */
 73 public class Method {
 74 
 75     /** The method name. */
 76     private final String name;
 77 
 78     /** The method descriptor. */
 79     private final String descriptor;
 80 
 81     /** The descriptors of the primitive Java types (plus void). */
 82     private static final Map<String, String> PRIMITIVE_TYPE_DESCRIPTORS;
 83 
 84     static {
 85         HashMap<String, String> descriptors = new HashMap<>();
 86         descriptors.put("void", "V");
 87         descriptors.put("byte", "B");
 88         descriptors.put("char", "C");
 89         descriptors.put("double", "D");
 90         descriptors.put("float", "F");
 91         descriptors.put("int", "I");
 92         descriptors.put("long", "J");
 93         descriptors.put("short", "S");
 94         descriptors.put("boolean", "Z");
 95         PRIMITIVE_TYPE_DESCRIPTORS = descriptors;
 96     }
 97 
 98     /**
 99       * Constructs a new {@link Method}.
100       *
101       * @param name the method's name.
102       * @param descriptor the method's descriptor.
103       */
104     public Method(final String name, final String descriptor) {
105         this.name = name;
106         this.descriptor = descriptor;
107     }
108 
109     /**
110       * Constructs a new {@link Method}.
111       *
112       * @param name the method's name.
113       * @param returnType the method's return type.
114       * @param argumentTypes the method's argument types.
115       */
116     public Method(final String name, final Type returnType, final Type[] argumentTypes) {
117         this(name, Type.getMethodDescriptor(returnType, argumentTypes));
118     }
119 
120     /**
121       * Creates a new {@link Method}.
122       *
123       * @param method a java.lang.reflect method descriptor
124       * @return a {@link Method} corresponding to the given Java method declaration.
125       */
126     public static Method getMethod(final java.lang.reflect.Method method) {
127         return new Method(method.getName(), Type.getMethodDescriptor(method));
128     }
129 
130     /**
131       * Creates a new {@link Method}.
132       *
133       * @param constructor a java.lang.reflect constructor descriptor
134       * @return a {@link Method} corresponding to the given Java constructor declaration.
135       */
136     public static Method getMethod(final java.lang.reflect.Constructor<?> constructor) {
137         return new Method("<init>", Type.getConstructorDescriptor(constructor));
138     }
139 
140     /**
141       * Returns a {@link Method} corresponding to the given Java method declaration.
142       *
143       * @param method a Java method declaration, without argument names, of the form "returnType name
144       *     (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int",
145       *     "float", "java.util.List", ...). Classes of the java.lang package can be specified by their
146       *     unqualified name; all other classes names must be fully qualified.
147       * @return a {@link Method} corresponding to the given Java method declaration.
148       * @throws IllegalArgumentException if <code>method</code> could not get parsed.
149       */
150     public static Method getMethod(final String method) {
151         return getMethod(method, false);
152     }
153 
154     /**
155       * Returns a {@link Method} corresponding to the given Java method declaration.
156       *
157       * @param method a Java method declaration, without argument names, of the form "returnType name
158       *     (argumentType1, ... argumentTypeN)", where the types are in plain Java (e.g. "int",
159       *     "float", "java.util.List", ...). Classes of the java.lang package may be specified by their
160       *     unqualified name, depending on the defaultPackage argument; all other classes names must be
161       *     fully qualified.
162       * @param defaultPackage true if unqualified class names belong to the default package, or false
163       *     if they correspond to java.lang classes. For instance "Object" means "Object" if this
164       *     option is true, or "java.lang.Object" otherwise.
165       * @return a {@link Method} corresponding to the given Java method declaration.
166       * @throws IllegalArgumentException if <code>method</code> could not get parsed.
167       */
168     public static Method getMethod(final String method, final boolean defaultPackage) {
169         final int spaceIndex = method.indexOf(' ');
170         int currentArgumentStartIndex = method.indexOf('(', spaceIndex) + 1;
171         final int endIndex = method.indexOf(')', currentArgumentStartIndex);
172         if (spaceIndex == -1 || currentArgumentStartIndex == 0 || endIndex == -1) {
173             throw new IllegalArgumentException();
174         }
175         final String returnType = method.substring(0, spaceIndex);
176         final String methodName =
177                 method.substring(spaceIndex + 1, currentArgumentStartIndex - 1).trim();
178         StringBuilder stringBuilder = new StringBuilder();
179         stringBuilder.append('(');
180         int currentArgumentEndIndex;
181         do {
182             String argumentDescriptor;
183             currentArgumentEndIndex = method.indexOf(',', currentArgumentStartIndex);
184             if (currentArgumentEndIndex == -1) {
185                 argumentDescriptor =
186                         getDescriptorInternal(
187                                 method.substring(currentArgumentStartIndex, endIndex).trim(), defaultPackage);
188             } else {
189                 argumentDescriptor =
190                         getDescriptorInternal(
191                                 method.substring(currentArgumentStartIndex, currentArgumentEndIndex).trim(),
192                                 defaultPackage);
193                 currentArgumentStartIndex = currentArgumentEndIndex + 1;
194             }
195             stringBuilder.append(argumentDescriptor);
196         } while (currentArgumentEndIndex != -1);
197         stringBuilder.append(')').append(getDescriptorInternal(returnType, defaultPackage));
198         return new Method(methodName, stringBuilder.toString());
199     }
200 
201     /**
202       * Returns the descriptor corresponding to the given type name.
203       *
204       * @param type a Java type name.
205       * @param defaultPackage true if unqualified class names belong to the default package, or false
206       *     if they correspond to java.lang classes. For instance "Object" means "Object" if this
207       *     option is true, or "java.lang.Object" otherwise.
208       * @return the descriptor corresponding to the given type name.
209       */
210     private static String getDescriptorInternal(final String type, final boolean defaultPackage) {
211         if ("".equals(type)) {
212             return type;
213         }
214 
215         StringBuilder stringBuilder = new StringBuilder();
216         int arrayBracketsIndex = 0;
217         while ((arrayBracketsIndex = type.indexOf("[]", arrayBracketsIndex) + 1) > 0) {
218             stringBuilder.append('[');
219         }
220 
221         String elementType = type.substring(0, type.length() - stringBuilder.length() * 2);
222         String descriptor = PRIMITIVE_TYPE_DESCRIPTORS.get(elementType);
223         if (descriptor != null) {
224             stringBuilder.append(descriptor);
225         } else {
226             // FIXME: support Q-type
227             stringBuilder.append('L');
228             if (elementType.indexOf('.') < 0) {
229                 if (!defaultPackage) {
230                     stringBuilder.append("java/lang/");
231                 }
232                 stringBuilder.append(elementType);
233             } else {
234                 stringBuilder.append(elementType.replace('.', '/'));
235             }
236             stringBuilder.append(';');
237         }
238         return stringBuilder.toString();
239     }
240 
241     /**
242       * Returns the name of the method described by this object.
243       *
244       * @return the name of the method described by this object.
245       */
246     public String getName() {
247         return name;
248     }
249 
250     /**
251       * Returns the descriptor of the method described by this object.
252       *
253       * @return the descriptor of the method described by this object.
254       */
255     public String getDescriptor() {
256         return descriptor;
257     }
258 
259     /**
260       * Returns the return type of the method described by this object.
261       *
262       * @return the return type of the method described by this object.
263       */
264     public Type getReturnType() {
265         return Type.getReturnType(descriptor);
266     }
267 
268     /**
269       * Returns the argument types of the method described by this object.
270       *
271       * @return the argument types of the method described by this object.
272       */
273     public Type[] getArgumentTypes() {
274         return Type.getArgumentTypes(descriptor);
275     }
276 
277     @Override
278     public String toString() {
279         return name + descriptor;
280     }
281 
282     @Override
283     public boolean equals(final Object other) {
284         if (!(other instanceof Method)) {
285             return false;
286         }
287         Method otherMethod = (Method) other;
288         return name.equals(otherMethod.name) && descriptor.equals(otherMethod.descriptor);
289     }
290 
291     @Override
292     public int hashCode() {
293         return name.hashCode() ^ descriptor.hashCode();
294     }
295 }
296