/* |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
/* |
|
* This file is available under and governed by the GNU General Public |
|
* License version 2 only, as published by the Free Software Foundation. |
|
* However, the following notice accompanied the original version of this |
|
* file: |
|
* |
|
* ASM: a very small and fast Java bytecode manipulation framework |
|
* Copyright (c) 2000-2011 INRIA, France Telecom |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. Neither the name of the copyright holders nor the names of its |
|
* contributors may be used to endorse or promote products derived from |
|
* this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
|
* THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
package jdk.internal.org.objectweb.asm; |
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.Method; |
|
/** |
|
* A Java field or method type. This class can be used to make it easier to manipulate type and |
|
* method descriptors. |
|
* |
|
* @author Eric Bruneton |
|
* @author Chris Nokleberg |
|
*/ |
|
public final class Type { |
|
/** The sort of the {@code void} type. See {@link #getSort}. */ |
|
public static final int VOID = 0; |
|
/** The sort of the {@code boolean} type. See {@link #getSort}. */ |
|
public static final int BOOLEAN = 1; |
|
/** The sort of the {@code char} type. See {@link #getSort}. */ |
|
public static final int CHAR = 2; |
|
/** The sort of the {@code byte} type. See {@link #getSort}. */ |
|
public static final int BYTE = 3; |
|
/** The sort of the {@code short} type. See {@link #getSort}. */ |
|
public static final int SHORT = 4; |
|
/** The sort of the {@code int} type. See {@link #getSort}. */ |
|
public static final int INT = 5; |
|
/** The sort of the {@code float} type. See {@link #getSort}. */ |
|
public static final int FLOAT = 6; |
|
/** The sort of the {@code long} type. See {@link #getSort}. */ |
|
public static final int LONG = 7; |
|
/** The sort of the {@code double} type. See {@link #getSort}. */ |
|
public static final int DOUBLE = 8; |
|
/** The sort of array reference types. See {@link #getSort}. */ |
|
public static final int ARRAY = 9; |
|
/** The sort of object reference types. See {@link #getSort}. */ |
|
public static final int OBJECT = 10; |
|
/** The sort of method types. See {@link #getSort}. */ |
|
public static final int METHOD = 11; |
|
/** The (private) sort of object reference types represented with an internal name. */ |
|
private static final int INTERNAL = 12; |
|
/** The descriptors of the primitive types. */ |
|
private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD"; |
|
/** The {@code void} type. */ |
|
public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1); |
|
/** The {@code boolean} type. */ |
|
public static final Type BOOLEAN_TYPE = |
|
new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1); |
|
/** The {@code char} type. */ |
|
public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1); |
|
/** The {@code byte} type. */ |
|
public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1); |
|
/** The {@code short} type. */ |
|
public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1); |
|
/** The {@code int} type. */ |
|
public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1); |
|
/** The {@code float} type. */ |
|
public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1); |
|
/** The {@code long} type. */ |
|
public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1); |
|
/** The {@code double} type. */ |
|
public static final Type DOUBLE_TYPE = |
|
new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1); |
|
// ----------------------------------------------------------------------------------------------- |
|
// Fields |
|
// ----------------------------------------------------------------------------------------------- |
|
/** |
|
* The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, |
|
* {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, |
|
* {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}. |
|
*/ |
|
private final int sort; |
|
/** |
|
* A buffer containing the value of this field or method type. This value is an internal name for |
|
* {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other |
|
* cases. |
|
* |
|
* <p>For {@link #OBJECT} types, this field also contains the descriptor: the characters in |
|
* [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link |
|
* #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor. |
|
*/ |
|
private final String valueBuffer; |
|
/** |
|
* The beginning index, inclusive, of the value of this Java field or method type in {@link |
|
* #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, |
|
* and a field or method descriptor in the other cases. |
|
*/ |
|
private final int valueBegin; |
|
/** |
|
* The end index, exclusive, of the value of this Java field or method type in {@link |
|
* #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, |
|
* and a field or method descriptor in the other cases. |
|
*/ |
|
private final int valueEnd; |
|
/** |
|
* Constructs a reference type. |
|
* |
|
* @param sort the sort of this type, see {@link #sort}. |
|
* @param valueBuffer a buffer containing the value of this field or method type. |
|
* @param valueBegin the beginning index, inclusive, of the value of this field or method type in |
|
* valueBuffer. |
|
* @param valueEnd the end index, exclusive, of the value of this field or method type in |
|
* valueBuffer. |
|
*/ |
|
private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) { |
|
this.sort = sort; |
|
this.valueBuffer = valueBuffer; |
|
this.valueBegin = valueBegin; |
|
this.valueEnd = valueEnd; |
|
} |
|
// ----------------------------------------------------------------------------------------------- |
|
// Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc. |
|
// ----------------------------------------------------------------------------------------------- |
|
/** |
|
* Returns the {@link Type} corresponding to the given type descriptor. |
|
* |
|
* @param typeDescriptor a field or method type descriptor. |
|
* @return the {@link Type} corresponding to the given type descriptor. |
|
*/ |
|
public static Type getType(final String typeDescriptor) { |
|
return getTypeInternal(typeDescriptor, 0, typeDescriptor.length()); |
|
} |
|
/** |
|
* Returns the {@link Type} corresponding to the given class. |
|
* |
|
* @param clazz a class. |
|
* @return the {@link Type} corresponding to the given class. |
|
*/ |
|
public static Type getType(final Class<?> clazz) { |
|
if (clazz.isPrimitive()) { |
|
if (clazz == Integer.TYPE) { |
|
return INT_TYPE; |
|
} else if (clazz == Void.TYPE) { |
|
return VOID_TYPE; |
|
} else if (clazz == Boolean.TYPE) { |
|
return BOOLEAN_TYPE; |
|
} else if (clazz == Byte.TYPE) { |
|
return BYTE_TYPE; |
|
} else if (clazz == Character.TYPE) { |
|
return CHAR_TYPE; |
|
} else if (clazz == Short.TYPE) { |
|
return SHORT_TYPE; |
|
} else if (clazz == Double.TYPE) { |
|
return DOUBLE_TYPE; |
|
} else if (clazz == Float.TYPE) { |
|
return FLOAT_TYPE; |
|
} else if (clazz == Long.TYPE) { |
|
return LONG_TYPE; |
|
} else { |
|
throw new AssertionError(); |
|
} |
|
} else { |
|
return getType(getDescriptor(clazz)); |
|
} |
|
} |
|
/** |
|
* Returns the method {@link Type} corresponding to the given constructor. |
|
* |
|
* @param constructor a {@link Constructor} object. |
|
* @return the method {@link Type} corresponding to the given constructor. |
|
*/ |
|
public static Type getType(final Constructor<?> constructor) { |
|
return getType(getConstructorDescriptor(constructor)); |
|
} |
|
/** |
|
* Returns the method {@link Type} corresponding to the given method. |
|
* |
|
* @param method a {@link Method} object. |
|
* @return the method {@link Type} corresponding to the given method. |
|
*/ |
|
public static Type getType(final Method method) { |
|
return getType(getMethodDescriptor(method)); |
|
} |
|
/** |
|
* Returns the type of the elements of this array type. This method should only be used for an |
|
* array type. |
|
* |
|
* @return Returns the type of the elements of this array type. |
|
*/ |
|
public Type getElementType() { |
|
final int numDimensions = getDimensions(); |
|
return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd); |
|
} |
|
/** |
|
* Returns the {@link Type} corresponding to the given internal name. |
|
* |
|
* @param internalName an internal name. |
|
* @return the {@link Type} corresponding to the given internal name. |
|
*/ |
|
public static Type getObjectType(final String internalName) { |
|
return new Type( |
|
internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length()); |
|
} |
|
/** |
|
* Returns the {@link Type} corresponding to the given method descriptor. Equivalent to <code> |
|
* Type.getType(methodDescriptor)</code>. |
|
* |
|
* @param methodDescriptor a method descriptor. |
|
* @return the {@link Type} corresponding to the given method descriptor. |
|
*/ |
|
public static Type getMethodType(final String methodDescriptor) { |
|
return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length()); |
|
} |
|
/** |
|
* Returns the method {@link Type} corresponding to the given argument and return types. |
|
* |
|
* @param returnType the return type of the method. |
|
* @param argumentTypes the argument types of the method. |
|
* @return the method {@link Type} corresponding to the given argument and return types. |
|
*/ |
|
public static Type getMethodType(final Type returnType, final Type... argumentTypes) { |
|
return getType(getMethodDescriptor(returnType, argumentTypes)); |
|
} |
|
/** |
|
* Returns the argument types of methods of this type. This method should only be used for method |
|
* types. |
|
* |
|
* @return the argument types of methods of this type. |
|
*/ |
|
public Type[] getArgumentTypes() { |
|
return getArgumentTypes(getDescriptor()); |
|
} |
|
/** |
|
* Returns the {@link Type} values corresponding to the argument types of the given method |
|
* descriptor. |
|
* |
|
* @param methodDescriptor a method descriptor. |
|
* @return the {@link Type} values corresponding to the argument types of the given method |
|
* descriptor. |
|
*/ |
|
public static Type[] getArgumentTypes(final String methodDescriptor) { |
|
// First step: compute the number of argument types in methodDescriptor. |
|
int numArgumentTypes = 0; |
|
// Skip the first character, which is always a '('. |
|
int currentOffset = 1; |
|
// Parse the argument types, one at a each loop iteration. |
|
while (methodDescriptor.charAt(currentOffset) != ')') { |
|
while (methodDescriptor.charAt(currentOffset) == '[') { |
|
currentOffset++; |
|
} |
|
if (methodDescriptor.charAt(currentOffset++) == 'L') { |
|
// Skip the argument descriptor content. |
|
int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset); |
|
currentOffset = Math.max(currentOffset, semiColumnOffset + 1); |
|
} |
|
++numArgumentTypes; |
|
} |
|
// Second step: create a Type instance for each argument type. |
|
Type[] argumentTypes = new Type[numArgumentTypes]; |
|
// Skip the first character, which is always a '('. |
|
currentOffset = 1; |
|
// Parse and create the argument types, one at each loop iteration. |
|
int currentArgumentTypeIndex = 0; |
|
while (methodDescriptor.charAt(currentOffset) != ')') { |
|
final int currentArgumentTypeOffset = currentOffset; |
|
while (methodDescriptor.charAt(currentOffset) == '[') { |
|
currentOffset++; |
|
} |
|
if (methodDescriptor.charAt(currentOffset++) == 'L') { |
|
// Skip the argument descriptor content. |
|
int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset); |
|
currentOffset = Math.max(currentOffset, semiColumnOffset + 1); |
|
} |
|
argumentTypes[currentArgumentTypeIndex++] = |
|
getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset); |
|
} |
|
return argumentTypes; |
|
} |
|
/** |
|
* Returns the {@link Type} values corresponding to the argument types of the given method. |
|
* |
|
* @param method a method. |
|
* @return the {@link Type} values corresponding to the argument types of the given method. |
|
*/ |
|
public static Type[] getArgumentTypes(final Method method) { |
|
Class<?>[] classes = method.getParameterTypes(); |
|
Type[] types = new Type[classes.length]; |
|
for (int i = classes.length - 1; i >= 0; --i) { |
|
types[i] = getType(classes[i]); |
|
} |
|
return types; |
|
} |
|
/** |
|
* Returns the return type of methods of this type. This method should only be used for method |
|
* types. |
|
* |
|
* @return the return type of methods of this type. |
|
*/ |
|
public Type getReturnType() { |
|
return getReturnType(getDescriptor()); |
|
} |
|
/** |
|
* Returns the {@link Type} corresponding to the return type of the given method descriptor. |
|
* |
|
* @param methodDescriptor a method descriptor. |
|
* @return the {@link Type} corresponding to the return type of the given method descriptor. |
|
*/ |
|
public static Type getReturnType(final String methodDescriptor) { |
|
return getTypeInternal( |
|
methodDescriptor, getReturnTypeOffset(methodDescriptor), methodDescriptor.length()); |
|
} |
|
/** |
|
* Returns the {@link Type} corresponding to the return type of the given method. |
|
* |
|
* @param method a method. |
|
* @return the {@link Type} corresponding to the return type of the given method. |
|
*/ |
|
public static Type getReturnType(final Method method) { |
|
return getType(method.getReturnType()); |
|
} |
|
/** |
|
* Returns the start index of the return type of the given method descriptor. |
|
* |
|
* @param methodDescriptor a method descriptor. |
|
* @return the start index of the return type of the given method descriptor. |
|
*/ |
|
static int getReturnTypeOffset(final String methodDescriptor) { |
|
// Skip the first character, which is always a '('. |
|
int currentOffset = 1; |
|
// Skip the argument types, one at a each loop iteration. |
|
while (methodDescriptor.charAt(currentOffset) != ')') { |
|
while (methodDescriptor.charAt(currentOffset) == '[') { |
|
currentOffset++; |
|
} |
|
if (methodDescriptor.charAt(currentOffset++) == 'L') { |
|
// Skip the argument descriptor content. |
|
int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset); |
|
currentOffset = Math.max(currentOffset, semiColumnOffset + 1); |
|
} |
|
} |
|
return currentOffset + 1; |
|
} |
|
/** |
|
* Returns the {@link Type} corresponding to the given field or method descriptor. |
|
* |
|
* @param descriptorBuffer a buffer containing the field or method descriptor. |
|
* @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in |
|
* descriptorBuffer. |
|
* @param descriptorEnd the end index, exclusive, of the field or method descriptor in |
|
* descriptorBuffer. |
|
* @return the {@link Type} corresponding to the given type descriptor. |
|
*/ |
|
private static Type getTypeInternal( |
|
final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) { |
|
switch (descriptorBuffer.charAt(descriptorBegin)) { |
|
case 'V': |
|
return VOID_TYPE; |
|
case 'Z': |
|
return BOOLEAN_TYPE; |
|
case 'C': |
|
return CHAR_TYPE; |
|
case 'B': |
|
return BYTE_TYPE; |
|
case 'S': |
|
return SHORT_TYPE; |
|
case 'I': |
|
return INT_TYPE; |
|
case 'F': |
|
return FLOAT_TYPE; |
|
case 'J': |
|
return LONG_TYPE; |
|
case 'D': |
|
return DOUBLE_TYPE; |
|
case '[': |
|
return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd); |
|
case 'L': |
|
return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1); |
|
case '(': |
|
return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd); |
|
default: |
|
throw new IllegalArgumentException(); |
|
} |
|
} |
|
// ----------------------------------------------------------------------------------------------- |
|
// Methods to get class names, internal names or descriptors. |
|
// ----------------------------------------------------------------------------------------------- |
|
/** |
|
* Returns the binary name of the class corresponding to this type. This method must not be used |
|
* on method types. |
|
* |
|
* @return the binary name of the class corresponding to this type. |
|
*/ |
|
public String getClassName() { |
|
switch (sort) { |
|
case VOID: |
|
return "void"; |
|
case BOOLEAN: |
|
return "boolean"; |
|
case CHAR: |
|
return "char"; |
|
case BYTE: |
|
return "byte"; |
|
case SHORT: |
|
return "short"; |
|
case INT: |
|
return "int"; |
|
case FLOAT: |
|
return "float"; |
|
case LONG: |
|
return "long"; |
|
case DOUBLE: |
|
return "double"; |
|
case ARRAY: |
|
StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName()); |
|
for (int i = getDimensions(); i > 0; --i) { |
|
stringBuilder.append("[]"); |
|
} |
|
return stringBuilder.toString(); |
|
case OBJECT: |
|
case INTERNAL: |
|
return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.'); |
|
default: |
|
throw new AssertionError(); |
|
} |
|
} |
|
/** |
|
* Returns the internal name of the class corresponding to this object or array type. The internal |
|
* name of a class is its fully qualified name (as returned by Class.getName(), where '.' are |
|
* replaced by '/'). This method should only be used for an object or array type. |
|
* |
|
* @return the internal name of the class corresponding to this object type. |
|
*/ |
|
public String getInternalName() { |
|
return valueBuffer.substring(valueBegin, valueEnd); |
|
} |
|
/** |
|
* Returns the internal name of the given class. The internal name of a class is its fully |
|
* qualified name, as returned by Class.getName(), where '.' are replaced by '/'. |
|
* |
|
* @param clazz an object or array class. |
|
* @return the internal name of the given class. |
|
*/ |
|
public static String getInternalName(final Class<?> clazz) { |
|
return clazz.getName().replace('.', '/'); |
|
} |
|
/** |
|
* Returns the descriptor corresponding to this type. |
|
* |
|
* @return the descriptor corresponding to this type. |
|
*/ |
|
public String getDescriptor() { |
|
if (sort == OBJECT) { |
|
return valueBuffer.substring(valueBegin - 1, valueEnd + 1); |
|
} else if (sort == INTERNAL) { |
|
return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';'; |
|
} else { |
|
return valueBuffer.substring(valueBegin, valueEnd); |
|
} |
|
} |
|
/** |
|
* Returns the descriptor corresponding to the given class. |
|
* |
|
* @param clazz an object class, a primitive class or an array class. |
|
* @return the descriptor corresponding to the given class. |
|
*/ |
|
public static String getDescriptor(final Class<?> clazz) { |
|
StringBuilder stringBuilder = new StringBuilder(); |
|
appendDescriptor(clazz, stringBuilder); |
|
return stringBuilder.toString(); |
|
} |
|
/** |
|
* Returns the descriptor corresponding to the given constructor. |
|
* |
|
* @param constructor a {@link Constructor} object. |
|
* @return the descriptor of the given constructor. |
|
*/ |
|
public static String getConstructorDescriptor(final Constructor<?> constructor) { |
|
StringBuilder stringBuilder = new StringBuilder(); |
|
stringBuilder.append('('); |
|
Class<?>[] parameters = constructor.getParameterTypes(); |
|
for (Class<?> parameter : parameters) { |
|
appendDescriptor(parameter, stringBuilder); |
|
} |
|
return stringBuilder.append(")V").toString(); |
|
} |
|
/** |
|
* Returns the descriptor corresponding to the given argument and return types. |
|
* |
|
* @param returnType the return type of the method. |
|
* @param argumentTypes the argument types of the method. |
|
* @return the descriptor corresponding to the given argument and return types. |
|
*/ |
|
public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) { |
|
StringBuilder stringBuilder = new StringBuilder(); |
|
stringBuilder.append('('); |
|
for (Type argumentType : argumentTypes) { |
|
argumentType.appendDescriptor(stringBuilder); |
|
} |
|
stringBuilder.append(')'); |
|
returnType.appendDescriptor(stringBuilder); |
|
return stringBuilder.toString(); |
|
} |
|
/** |
|
* Returns the descriptor corresponding to the given method. |
|
* |
|
* @param method a {@link Method} object. |
|
* @return the descriptor of the given method. |
|
*/ |
|
public static String getMethodDescriptor(final Method method) { |
|
StringBuilder stringBuilder = new StringBuilder(); |
|
stringBuilder.append('('); |
|
Class<?>[] parameters = method.getParameterTypes(); |
|
for (Class<?> parameter : parameters) { |
|
appendDescriptor(parameter, stringBuilder); |
|
} |
|
stringBuilder.append(')'); |
|
appendDescriptor(method.getReturnType(), stringBuilder); |
|
return stringBuilder.toString(); |
|
} |
|
/** |
|
* Appends the descriptor corresponding to this type to the given string buffer. |
|
* |
|
* @param stringBuilder the string builder to which the descriptor must be appended. |
|
*/ |
|
private void appendDescriptor(final StringBuilder stringBuilder) { |
|
if (sort == OBJECT) { |
|
stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1); |
|
} else if (sort == INTERNAL) { |
|
stringBuilder.append('L').append(valueBuffer, valueBegin, valueEnd).append(';'); |
|
} else { |
|
stringBuilder.append(valueBuffer, valueBegin, valueEnd); |
|
} |
|
} |
|
/** |
|
* Appends the descriptor of the given class to the given string builder. |
|
* |
|
* @param clazz the class whose descriptor must be computed. |
|
* @param stringBuilder the string builder to which the descriptor must be appended. |
|
*/ |
|
private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) { |
|
Class<?> currentClass = clazz; |
|
while (currentClass.isArray()) { |
|
stringBuilder.append('['); |
|
currentClass = currentClass.getComponentType(); |
|
} |
|
if (currentClass.isPrimitive()) { |
|
char descriptor; |
|
if (currentClass == Integer.TYPE) { |
|
descriptor = 'I'; |
|
} else if (currentClass == Void.TYPE) { |
|
descriptor = 'V'; |
|
} else if (currentClass == Boolean.TYPE) { |
|
descriptor = 'Z'; |
|
} else if (currentClass == Byte.TYPE) { |
|
descriptor = 'B'; |
|
} else if (currentClass == Character.TYPE) { |
|
descriptor = 'C'; |
|
} else if (currentClass == Short.TYPE) { |
|
descriptor = 'S'; |
|
} else if (currentClass == Double.TYPE) { |
|
descriptor = 'D'; |
|
} else if (currentClass == Float.TYPE) { |
|
descriptor = 'F'; |
|
} else if (currentClass == Long.TYPE) { |
|
descriptor = 'J'; |
|
} else { |
|
throw new AssertionError(); |
|
} |
|
stringBuilder.append(descriptor); |
|
} else { |
|
stringBuilder.append('L').append(getInternalName(currentClass)).append(';'); |
|
} |
|
} |
|
// ----------------------------------------------------------------------------------------------- |
|
// Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor. |
|
// ----------------------------------------------------------------------------------------------- |
|
/** |
|
* Returns the sort of this type. |
|
* |
|
* @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link |
|
* #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or |
|
* {@link #METHOD}. |
|
*/ |
|
public int getSort() { |
|
return sort == INTERNAL ? OBJECT : sort; |
|
} |
|
/** |
|
* Returns the number of dimensions of this array type. This method should only be used for an |
|
* array type. |
|
* |
|
* @return the number of dimensions of this array type. |
|
*/ |
|
public int getDimensions() { |
|
int numDimensions = 1; |
|
while (valueBuffer.charAt(valueBegin + numDimensions) == '[') { |
|
numDimensions++; |
|
} |
|
return numDimensions; |
|
} |
|
/** |
|
* Returns the size of values of this type. This method must not be used for method types. |
|
* |
|
* @return the size of values of this type, i.e., 2 for {@code long} and {@code double}, 0 for |
|
* {@code void} and 1 otherwise. |
|
*/ |
|
public int getSize() { |
|
switch (sort) { |
|
case VOID: |
|
return 0; |
|
case BOOLEAN: |
|
case CHAR: |
|
case BYTE: |
|
case SHORT: |
|
case INT: |
|
case FLOAT: |
|
case ARRAY: |
|
case OBJECT: |
|
case INTERNAL: |
|
return 1; |
|
case LONG: |
|
case DOUBLE: |
|
return 2; |
|
default: |
|
throw new AssertionError(); |
|
} |
|
} |
|
/** |
|
* Returns the size of the arguments and of the return value of methods of this type. This method |
|
* should only be used for method types. |
|
* |
|
* @return the size of the arguments of the method (plus one for the implicit this argument), |
|
* argumentsSize, and the size of its return value, returnSize, packed into a single int i = |
|
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code |
|
* i >> 2}, and returnSize to {@code i & 0x03}). |
|
*/ |
|
public int getArgumentsAndReturnSizes() { |
|
return getArgumentsAndReturnSizes(getDescriptor()); |
|
} |
|
/** |
|
* Computes the size of the arguments and of the return value of a method. |
|
* |
|
* @param methodDescriptor a method descriptor. |
|
* @return the size of the arguments of the method (plus one for the implicit this argument), |
|
* argumentsSize, and the size of its return value, returnSize, packed into a single int i = |
|
* {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code |
|
* i >> 2}, and returnSize to {@code i & 0x03}). |
|
*/ |
|
public static int getArgumentsAndReturnSizes(final String methodDescriptor) { |
|
int argumentsSize = 1; |
|
// Skip the first character, which is always a '('. |
|
int currentOffset = 1; |
|
int currentChar = methodDescriptor.charAt(currentOffset); |
|
// Parse the argument types and compute their size, one at a each loop iteration. |
|
while (currentChar != ')') { |
|
if (currentChar == 'J' || currentChar == 'D') { |
|
currentOffset++; |
|
argumentsSize += 2; |
|
} else { |
|
while (methodDescriptor.charAt(currentOffset) == '[') { |
|
currentOffset++; |
|
} |
|
if (methodDescriptor.charAt(currentOffset++) == 'L') { |
|
// Skip the argument descriptor content. |
|
int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset); |
|
currentOffset = Math.max(currentOffset, semiColumnOffset + 1); |
|
} |
|
argumentsSize += 1; |
|
} |
|
currentChar = methodDescriptor.charAt(currentOffset); |
|
} |
|
currentChar = methodDescriptor.charAt(currentOffset + 1); |
|
if (currentChar == 'V') { |
|
return argumentsSize << 2; |
|
} else { |
|
int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1; |
|
return argumentsSize << 2 | returnSize; |
|
} |
|
} |
|
/** |
|
* Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for |
|
* method types. |
|
* |
|
* @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, |
|
* IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and |
|
* IRETURN. |
|
* @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For |
|
* example, if this type is {@code float} and {@code opcode} is IRETURN, this method returns |
|
* FRETURN. |
|
*/ |
|
public int getOpcode(final int opcode) { |
|
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { |
|
switch (sort) { |
|
case BOOLEAN: |
|
case BYTE: |
|
return opcode + (Opcodes.BALOAD - Opcodes.IALOAD); |
|
case CHAR: |
|
return opcode + (Opcodes.CALOAD - Opcodes.IALOAD); |
|
case SHORT: |
|
return opcode + (Opcodes.SALOAD - Opcodes.IALOAD); |
|
case INT: |
|
return opcode; |
|
case FLOAT: |
|
return opcode + (Opcodes.FALOAD - Opcodes.IALOAD); |
|
case LONG: |
|
return opcode + (Opcodes.LALOAD - Opcodes.IALOAD); |
|
case DOUBLE: |
|
return opcode + (Opcodes.DALOAD - Opcodes.IALOAD); |
|
case ARRAY: |
|
case OBJECT: |
|
case INTERNAL: |
|
return opcode + (Opcodes.AALOAD - Opcodes.IALOAD); |
|
case METHOD: |
|
case VOID: |
|
throw new UnsupportedOperationException(); |
|
default: |
|
throw new AssertionError(); |
|
} |
|
} else { |
|
switch (sort) { |
|
case VOID: |
|
if (opcode != Opcodes.IRETURN) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
return Opcodes.RETURN; |
|
case BOOLEAN: |
|
case BYTE: |
|
case CHAR: |
|
case SHORT: |
|
case INT: |
|
return opcode; |
|
case FLOAT: |
|
return opcode + (Opcodes.FRETURN - Opcodes.IRETURN); |
|
case LONG: |
|
return opcode + (Opcodes.LRETURN - Opcodes.IRETURN); |
|
case DOUBLE: |
|
return opcode + (Opcodes.DRETURN - Opcodes.IRETURN); |
|
case ARRAY: |
|
case OBJECT: |
|
case INTERNAL: |
|
if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
return opcode + (Opcodes.ARETURN - Opcodes.IRETURN); |
|
case METHOD: |
|
throw new UnsupportedOperationException(); |
|
default: |
|
throw new AssertionError(); |
|
} |
|
} |
|
} |
|
// ----------------------------------------------------------------------------------------------- |
|
// Equals, hashCode and toString. |
|
// ----------------------------------------------------------------------------------------------- |
|
/** |
|
* Tests if the given object is equal to this type. |
|
* |
|
* @param object the object to be compared to this type. |
|
* @return {@literal true} if the given object is equal to this type. |
|
*/ |
|
@Override |
|
public boolean equals(final Object object) { |
|
if (this == object) { |
|
return true; |
|
} |
|
if (!(object instanceof Type)) { |
|
return false; |
|
} |
|
Type other = (Type) object; |
|
if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) { |
|
return false; |
|
} |
|
int begin = valueBegin; |
|
int end = valueEnd; |
|
int otherBegin = other.valueBegin; |
|
int otherEnd = other.valueEnd; |
|
// Compare the values. |
|
if (end - begin != otherEnd - otherBegin) { |
|
return false; |
|
} |
|
for (int i = begin, j = otherBegin; i < end; i++, j++) { |
|
if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
/** |
|
* Returns a hash code value for this type. |
|
* |
|
* @return a hash code value for this type. |
|
*/ |
|
@Override |
|
public int hashCode() { |
|
int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort); |
|
if (sort >= ARRAY) { |
|
for (int i = valueBegin, end = valueEnd; i < end; i++) { |
|
hashCode = 17 * (hashCode + valueBuffer.charAt(i)); |
|
} |
|
} |
|
return hashCode; |
|
} |
|
/** |
|
* Returns a string representation of this type. |
|
* |
|
* @return the descriptor of this type. |
|
*/ |
|
@Override |
|
public String toString() { |
|
return getDescriptor(); |
|
} |
|
} |