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