Mirror of the BLOAT repository
https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1035 lines
24 KiB
1035 lines
24 KiB
/**
|
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and
|
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
|
* Research Foundation of Purdue University. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package EDU.purdue.cs.bloat.editor;
|
|
|
|
import java.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.util.*;
|
|
|
|
/**
|
|
* Type represents a type descriptor in a classes constant pool. A type
|
|
* descriptor describes the type of a field of method using a funky encoding
|
|
* which is described in the Java Virtual Machine Specification. For example,
|
|
* the field <tt>int x[]</tt> has the type descriptor:
|
|
* <p>
|
|
* <center>[I</center>
|
|
* <p>
|
|
* The method <tt>String f(int a, boolean b, Object c)</tt> has the type
|
|
* descriptor:
|
|
* <p>
|
|
* <center>(IZLjava/lang/Object;)Ljava/lang/String;</center>
|
|
* <p>
|
|
*
|
|
* @see EDU.purdue.cs.bloat.reflect.Constant Constant
|
|
*
|
|
* @author Nate Nystrom (<a
|
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
|
|
*/
|
|
public class Type {
|
|
public static final char ARRAY_CHAR = '[';
|
|
|
|
public static final char BOOLEAN_CHAR = 'Z';
|
|
|
|
public static final char BYTE_CHAR = 'B';
|
|
|
|
public static final char CHARACTER_CHAR = 'C';
|
|
|
|
public static final char CLASS_CHAR = 'L';
|
|
|
|
public static final char DOUBLE_CHAR = 'D';
|
|
|
|
public static final char FLOAT_CHAR = 'F';
|
|
|
|
public static final char INTEGER_CHAR = 'I';
|
|
|
|
public static final char LONG_CHAR = 'J';
|
|
|
|
public static final char SHORT_CHAR = 'S';
|
|
|
|
public static final char VOID_CHAR = 'V';
|
|
|
|
public static final char ADDRESS_CHAR = 'A'; // return address
|
|
|
|
public static final int BOOLEAN_CODE = 4;
|
|
|
|
public static final int CHARACTER_CODE = 5;
|
|
|
|
public static final int FLOAT_CODE = 6;
|
|
|
|
public static final int DOUBLE_CODE = 7;
|
|
|
|
public static final int BYTE_CODE = 8;
|
|
|
|
public static final int SHORT_CODE = 9;
|
|
|
|
public static final int INTEGER_CODE = 10;
|
|
|
|
public static final int LONG_CODE = 11;
|
|
|
|
public static final Type OBJECT;
|
|
|
|
public static final Type STRING;
|
|
|
|
public static final Type CLASS;
|
|
|
|
public static final Type THROWABLE;
|
|
|
|
public static final Type CLONEABLE;
|
|
|
|
public static final Type SERIALIZABLE;
|
|
|
|
public static final Type NULL;
|
|
|
|
public static final Type BOOLEAN;
|
|
|
|
public static final Type CHARACTER;
|
|
|
|
public static final Type FLOAT;
|
|
|
|
public static final Type DOUBLE;
|
|
|
|
public static final Type BYTE;
|
|
|
|
public static final Type SHORT;
|
|
|
|
public static final Type INTEGER;
|
|
|
|
public static final Type LONG;
|
|
|
|
public static final Type VOID;
|
|
|
|
public static final Type ADDRESS;
|
|
|
|
/**
|
|
* Print truncated (abbreviated) type names.
|
|
*/
|
|
public static boolean PRINT_TRUNCATED = false;
|
|
|
|
private static Map types; // All types in existence
|
|
|
|
static {
|
|
Type.types = new TreeMap(); // log(n) get
|
|
|
|
OBJECT = Type.getType("Ljava/lang/Object;");
|
|
STRING = Type.getType("Ljava/lang/String;");
|
|
CLASS = Type.getType("Ljava/lang/Class;");
|
|
THROWABLE = Type.getType("Ljava/lang/Throwable;");
|
|
CLONEABLE = Type.getType("Ljava/lang/Cloneable;");
|
|
SERIALIZABLE = Type.getType("Ljava/lang/Serializable;");
|
|
NULL = Type.getType("Lnull!;");
|
|
BOOLEAN = Type.getType(Type.BOOLEAN_CHAR);
|
|
CHARACTER = Type.getType(Type.CHARACTER_CHAR);
|
|
FLOAT = Type.getType(Type.FLOAT_CHAR);
|
|
DOUBLE = Type.getType(Type.DOUBLE_CHAR);
|
|
BYTE = Type.getType(Type.BYTE_CHAR);
|
|
SHORT = Type.getType(Type.SHORT_CHAR);
|
|
INTEGER = Type.getType(Type.INTEGER_CHAR);
|
|
LONG = Type.getType(Type.LONG_CHAR);
|
|
VOID = Type.getType(Type.VOID_CHAR);
|
|
ADDRESS = Type.getType(Type.ADDRESS_CHAR);
|
|
}
|
|
|
|
String desc; // The descriptor of a Type.
|
|
|
|
// A descriptor is a String representation of a field or method.
|
|
// Descriptors of basics types consist of a single character code
|
|
// such as B (BYTE_CHAR) for byte and D (DOUBLE_CHAR) for double.
|
|
// An object type is represented by L<classname>; Array
|
|
// descriptors have a [ for each dimension followed by the character
|
|
// describing the type (e.g. [[[D for double d[][][]).
|
|
// Method descriptors consist of the descriptors of the method's
|
|
// parameters delimited by parentheses followed by the descript for
|
|
// the method's return type. For example, the descriptor for:
|
|
// Object m(int, double, Thread)
|
|
// is:
|
|
// (IDLjava/lang/Thread;)Ljava/lang/Object;
|
|
|
|
// If this is a type descriptor for a method, these are important
|
|
Type[] paramTypes; // Desriptors of parameters to a method
|
|
|
|
Type returnType; // Descriptor of the return type of a method
|
|
|
|
/**
|
|
* Returns a <tt>Type</tt> of a given descriptor. Equals descriptors will
|
|
* result in the same <tt>Type</tt>.
|
|
*/
|
|
public static Type getType(final String desc) {
|
|
// It might be the case that we're passed a string already in the
|
|
// form of a type descriptor. Remove any trailing ';' and leading
|
|
// 'L'
|
|
// if(desc.endsWith(";") && desc.startsWith("L")) {
|
|
// desc = desc.substring(1, desc.length() - 1);
|
|
// }
|
|
|
|
Type type = (Type) Type.types.get(desc);
|
|
if (type == null) {
|
|
type = new Type(desc);
|
|
Type.types.put(desc, type);
|
|
}
|
|
return (type);
|
|
}
|
|
|
|
/**
|
|
* Returns a <code>Type</code> that represents a given <code>Class</code>.
|
|
*/
|
|
public static Type getType(final Class c) {
|
|
if (c.equals(Boolean.TYPE)) {
|
|
return (Type.BOOLEAN);
|
|
|
|
} else if (c.equals(Character.TYPE)) {
|
|
return (Type.CHARACTER);
|
|
|
|
} else if (c.equals(Float.TYPE)) {
|
|
return (Type.FLOAT);
|
|
|
|
} else if (c.equals(Double.TYPE)) {
|
|
return (Type.DOUBLE);
|
|
|
|
} else if (c.equals(Byte.TYPE)) {
|
|
return (Type.BYTE);
|
|
|
|
} else if (c.equals(Short.TYPE)) {
|
|
return (Type.SHORT);
|
|
|
|
} else if (c.equals(Integer.TYPE)) {
|
|
return (Type.INTEGER);
|
|
|
|
} else if (c.equals(Long.TYPE)) {
|
|
return (Type.LONG);
|
|
|
|
} else if (c.equals(Void.TYPE)) {
|
|
return (Type.VOID);
|
|
}
|
|
|
|
// (pr)
|
|
String name = c.getName().replace('.', '/');
|
|
if (!c.isArray()) {
|
|
name = "L" + name + ";";
|
|
}
|
|
return (Type.getType(name));
|
|
}
|
|
|
|
/**
|
|
* Returns the Type for a method with the given parameter and return types
|
|
*/
|
|
public static Type getType(final Type[] paramTypes, final Type returnType) {
|
|
final StringBuffer sb = new StringBuffer("(");
|
|
for (int i = 0; i < paramTypes.length; i++) {
|
|
sb.append(paramTypes[i].descriptor());
|
|
}
|
|
sb.append(")");
|
|
sb.append(returnType.descriptor());
|
|
return (Type.getType(sb.toString()));
|
|
}
|
|
|
|
/**
|
|
* Constructor. Creates a type descriptor from a String.
|
|
*/
|
|
private Type(final String desc) {
|
|
final ArrayList types = new ArrayList();
|
|
String t = "";
|
|
boolean method = false;
|
|
|
|
this.desc = desc.intern();
|
|
|
|
int state = 0;
|
|
int i = 0;
|
|
|
|
// Basically, we're parsing the desc String to get information about
|
|
// the descriptor. For instance, method descriptors begin with '('.
|
|
|
|
if (desc.charAt(i) == '(') {
|
|
method = true;
|
|
i++;
|
|
}
|
|
|
|
for (; i < desc.length(); i++) {
|
|
switch (state) {
|
|
case 0:
|
|
// Parameter descriptor
|
|
switch (desc.charAt(i)) {
|
|
case BYTE_CHAR:
|
|
case CHARACTER_CHAR:
|
|
case DOUBLE_CHAR:
|
|
case FLOAT_CHAR:
|
|
case INTEGER_CHAR:
|
|
case LONG_CHAR:
|
|
case SHORT_CHAR:
|
|
case VOID_CHAR:
|
|
case BOOLEAN_CHAR:
|
|
types.add(t + desc.charAt(i));
|
|
t = "";
|
|
break;
|
|
case CLASS_CHAR:
|
|
t += desc.charAt(i);
|
|
state = 1;
|
|
break;
|
|
case ARRAY_CHAR:
|
|
t += desc.charAt(i);
|
|
break;
|
|
case ')':
|
|
if (!method) {
|
|
final String s = "Invalid char in type descriptor: )\n"
|
|
+ "Descriptor was: " + desc;
|
|
throw new IllegalArgumentException(s);
|
|
}
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid char in "
|
|
+ "type descriptor (" + desc + "): "
|
|
+ desc.charAt(i));
|
|
}
|
|
break;
|
|
|
|
case 1: // Class descriptor
|
|
t += desc.charAt(i);
|
|
if (desc.charAt(i) == ';') { // ';' terminates class
|
|
// descriptor
|
|
types.add(t);
|
|
t = "";
|
|
state = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (method) {
|
|
// Set up the parameter and return Type fields for a method
|
|
|
|
final int sizeM1 = types.size() - 1;
|
|
returnType = Type.getType((String) types.get(sizeM1));
|
|
paramTypes = new Type[sizeM1];
|
|
for (i = 0; i < sizeM1; i++) {
|
|
final String s = (String) types.get(i);
|
|
if (s != null) {
|
|
paramTypes[i] = Type.getType(s);
|
|
}
|
|
}
|
|
} else {
|
|
if (types.size() != 1) {
|
|
throw new IllegalArgumentException("More than one type in "
|
|
+ "the type descriptor: " + desc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a <tt>Type</tt> for a primitive type based on its integer "type
|
|
* code".
|
|
*/
|
|
public static Type getType(final int typeCode) {
|
|
/* This method is called by codegen.Codegen */
|
|
String desc = null;
|
|
|
|
switch (typeCode) {
|
|
case BOOLEAN_CODE:
|
|
desc = "" + Type.BOOLEAN_CHAR;
|
|
break;
|
|
case CHARACTER_CODE:
|
|
desc = "" + Type.CHARACTER_CHAR;
|
|
break;
|
|
case FLOAT_CODE:
|
|
desc = "" + Type.FLOAT_CHAR;
|
|
break;
|
|
case DOUBLE_CODE:
|
|
desc = "" + Type.DOUBLE_CHAR;
|
|
break;
|
|
case BYTE_CODE:
|
|
desc = "" + Type.BYTE_CHAR;
|
|
break;
|
|
case SHORT_CODE:
|
|
desc = "" + Type.SHORT_CHAR;
|
|
break;
|
|
case INTEGER_CODE:
|
|
desc = "" + Type.INTEGER_CHAR;
|
|
break;
|
|
case LONG_CODE:
|
|
desc = "" + Type.LONG_CHAR;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid type code: " + typeCode);
|
|
}
|
|
|
|
Type type = (Type) Type.types.get(desc);
|
|
if (type == null) {
|
|
type = new Type();
|
|
type.setDesc(desc);
|
|
Type.types.put(desc, type);
|
|
}
|
|
return (type);
|
|
}
|
|
|
|
/**
|
|
* Empty constructor.
|
|
*/
|
|
private Type() {
|
|
this.desc = null;
|
|
}
|
|
|
|
private void setDesc(final String desc) {
|
|
this.desc = desc;
|
|
}
|
|
|
|
/**
|
|
* Returns a <tt>Type</tt> of a primitive type based on a one-character
|
|
* type descriptor.
|
|
*/
|
|
public static Type getType(final char typeChar) {
|
|
String desc = null;
|
|
|
|
switch (typeChar) {
|
|
case BOOLEAN_CHAR:
|
|
case CHARACTER_CHAR:
|
|
case FLOAT_CHAR:
|
|
case DOUBLE_CHAR:
|
|
case BYTE_CHAR:
|
|
case SHORT_CHAR:
|
|
case INTEGER_CHAR:
|
|
case LONG_CHAR:
|
|
case ADDRESS_CHAR:
|
|
case VOID_CHAR:
|
|
desc = "" + typeChar;
|
|
desc = desc.intern();
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid type descriptor: "
|
|
+ typeChar);
|
|
}
|
|
|
|
Type type = (Type) Type.types.get(desc);
|
|
if (type == null) {
|
|
type = new Type();
|
|
type.setDesc(desc);
|
|
Type.types.put(desc, type);
|
|
}
|
|
return (type);
|
|
}
|
|
|
|
/**
|
|
* Get the type code of the type (which must be a primitive type).
|
|
*
|
|
* @return The type code of the type.
|
|
* @exception IllegalArgumentException
|
|
* If the type is not primitive.
|
|
*/
|
|
public int typeCode() {
|
|
if (desc.length() == 1) {
|
|
switch (desc.charAt(0)) {
|
|
case BOOLEAN_CHAR:
|
|
return Type.BOOLEAN_CODE;
|
|
case CHARACTER_CHAR:
|
|
return Type.CHARACTER_CODE;
|
|
case FLOAT_CHAR:
|
|
return Type.FLOAT_CODE;
|
|
case DOUBLE_CHAR:
|
|
return Type.DOUBLE_CODE;
|
|
case BYTE_CHAR:
|
|
return Type.BYTE_CODE;
|
|
case SHORT_CHAR:
|
|
return Type.SHORT_CODE;
|
|
case INTEGER_CHAR:
|
|
return Type.INTEGER_CODE;
|
|
case LONG_CHAR:
|
|
return Type.LONG_CODE;
|
|
}
|
|
}
|
|
|
|
throw new IllegalArgumentException("Invalid type descriptor: " + desc);
|
|
}
|
|
|
|
/**
|
|
* Get a one character name for the type.
|
|
*/
|
|
public String shortName() {
|
|
if (isIntegral()) {
|
|
return "" + Type.INTEGER_CHAR;
|
|
}
|
|
|
|
// Use R rather than L for readability.
|
|
if (isReference()) {
|
|
return "R";
|
|
}
|
|
|
|
Assert.isTrue(desc.length() == 1, "Short name too long: " + desc);
|
|
|
|
return desc;
|
|
}
|
|
|
|
/**
|
|
* Get a simplification of the type. All integral types become INTEGER. All
|
|
* reference types (objects, arrays, returnAddress) become OBJECT.
|
|
*
|
|
* @return The simplified type.
|
|
*/
|
|
public Type simple() {
|
|
if (isIntegral()) {
|
|
return Type.INTEGER;
|
|
}
|
|
|
|
if (isReference()) {
|
|
return Type.OBJECT;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Get a descriptor of the type.
|
|
*
|
|
* @return The type descriptor.
|
|
*/
|
|
public String descriptor() {
|
|
return desc;
|
|
}
|
|
|
|
/**
|
|
* Check if the type is a method type.
|
|
*
|
|
* @return true if a method type, false if not.
|
|
*/
|
|
public boolean isMethod() {
|
|
return returnType != null;
|
|
}
|
|
|
|
/**
|
|
* Check if the type is a null type.
|
|
*
|
|
* @return true if a null type, false if not.
|
|
*/
|
|
public boolean isNull() {
|
|
return equals(Type.NULL);
|
|
}
|
|
|
|
/**
|
|
* Check if the type is a void type.
|
|
*
|
|
* @return true if a void type, false if not.
|
|
*/
|
|
public boolean isVoid() {
|
|
return equals(Type.VOID);
|
|
}
|
|
|
|
/**
|
|
* Check if the type is a primitive type.
|
|
*
|
|
* @return true if a primitive type, false if not.
|
|
*/
|
|
public boolean isPrimitive() {
|
|
return !isReference() && !isMethod() && !isVoid();
|
|
}
|
|
|
|
/**
|
|
* Check if the type is an integral type. Integral contains BOOLEAN as far
|
|
* as the JVM is concerned.
|
|
*
|
|
* @return true if an integral type, false if not.
|
|
*/
|
|
public boolean isIntegral() {
|
|
return (desc.charAt(0) == Type.BYTE_CHAR)
|
|
|| (desc.charAt(0) == Type.SHORT_CHAR)
|
|
|| (desc.charAt(0) == Type.INTEGER_CHAR)
|
|
|| (desc.charAt(0) == Type.CHARACTER_CHAR)
|
|
|| (desc.charAt(0) == Type.BOOLEAN_CHAR);
|
|
}
|
|
|
|
/**
|
|
* Check if the type is an array type.
|
|
*
|
|
* @return true if an array type, false if not.
|
|
*/
|
|
public boolean isArray() {
|
|
return desc.charAt(0) == Type.ARRAY_CHAR;
|
|
}
|
|
|
|
/**
|
|
* Check if the type is an object type (not array).
|
|
*
|
|
* @return true if an object type, false if not.
|
|
*/
|
|
public boolean isObject() {
|
|
return desc.charAt(0) == Type.CLASS_CHAR;
|
|
}
|
|
|
|
/**
|
|
* Check if the type takes of 2 local variable or stack positions.
|
|
*
|
|
* @return true if a wide type, false if not.
|
|
*/
|
|
public boolean isWide() {
|
|
return (desc.charAt(0) == Type.LONG_CHAR)
|
|
|| (desc.charAt(0) == Type.DOUBLE_CHAR);
|
|
}
|
|
|
|
/**
|
|
* Check if the type is a returnAddress.
|
|
*
|
|
* @return true if a address type, false if not.
|
|
*/
|
|
public boolean isAddress() {
|
|
return desc.charAt(0) == Type.ADDRESS_CHAR;
|
|
}
|
|
|
|
/**
|
|
* Check if the type is an array or object.
|
|
*
|
|
* @return true if a reference type, false if not.
|
|
*/
|
|
public boolean isReference() {
|
|
return (desc.charAt(0) == Type.ARRAY_CHAR)
|
|
|| (desc.charAt(0) == Type.CLASS_CHAR);
|
|
}
|
|
|
|
/**
|
|
* Get the type descriptor of a class from a string representation.. For
|
|
* example "java/lang/String" becomes "Ljava/lang/String;"
|
|
*
|
|
* @param name
|
|
* The name of the class.
|
|
* @return The type descriptor of the class.
|
|
*/
|
|
public static String classDescriptor(String name) {
|
|
// The name of the class may arrive as the name of the class file
|
|
// in which in resides. In this case, we should remove the .class
|
|
// file extension.
|
|
if (name.endsWith(".class")) {
|
|
name = name.substring(0, name.lastIndexOf('.'));
|
|
}
|
|
|
|
name = name.replace('.', '/');
|
|
|
|
if (name.charAt(0) == Type.ARRAY_CHAR) {
|
|
return name;
|
|
}
|
|
|
|
return Type.CLASS_CHAR + name + ";";
|
|
}
|
|
|
|
/**
|
|
* Get the class name of the type. For example if the class descriptor is
|
|
* "Ljava/lang/String;", the class name is "java/lang/String".
|
|
*
|
|
* @return The class name of the type.
|
|
*/
|
|
public String className() {
|
|
if ((desc.charAt(0) == Type.ARRAY_CHAR) || this.isPrimitive()) {
|
|
return desc;
|
|
}
|
|
|
|
return desc.substring(1, desc.lastIndexOf(';'));
|
|
}
|
|
|
|
/**
|
|
* Get the qualifier of the type. For example if the class descriptor is
|
|
* "Ljava/lang/String;", the qualifier is "java/lang/".
|
|
*
|
|
* @return The qualifier of the type.
|
|
*/
|
|
public String qualifier() {
|
|
Assert.isTrue(desc.charAt(0) == Type.CLASS_CHAR, "No qualifier for "
|
|
+ desc);
|
|
|
|
final int index = desc.lastIndexOf('/');
|
|
|
|
if (index >= 0) {
|
|
return desc.substring(1, index);
|
|
}
|
|
|
|
return desc.substring(1, desc.lastIndexOf(';'));
|
|
}
|
|
|
|
/**
|
|
* Get the number of dimensions of an array type.
|
|
*/
|
|
public int dimensions() {
|
|
for (int i = 0; i < desc.length(); i++) {
|
|
if (desc.charAt(i) != Type.ARRAY_CHAR) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
throw new IllegalArgumentException(desc
|
|
+ " does not have an element type.");
|
|
}
|
|
|
|
/**
|
|
* Get a Type representing an array of this type.
|
|
*/
|
|
public Type arrayType() {
|
|
return Type.getType("" + Type.ARRAY_CHAR + desc);
|
|
}
|
|
|
|
/**
|
|
* Create a Type representing a multidimensional array of this type.
|
|
*/
|
|
public Type arrayType(int dimensions) {
|
|
String d = "";
|
|
|
|
while (dimensions-- > 0) {
|
|
d += Type.ARRAY_CHAR;
|
|
}
|
|
|
|
return Type.getType(d + desc);
|
|
}
|
|
|
|
/**
|
|
* Get the element type of this array type.
|
|
*
|
|
* @param dimensions
|
|
* The number of times to index into the array.
|
|
* @return The element type.
|
|
* @exception IllegalArgumentException
|
|
* If the type is not an array.
|
|
*/
|
|
public Type elementType(final int dimensions) {
|
|
for (int i = 0; i < dimensions; i++) {
|
|
if (desc.charAt(i) != Type.ARRAY_CHAR) {
|
|
throw new IllegalArgumentException(desc + " is not an array");
|
|
}
|
|
}
|
|
|
|
return Type.getType(desc.substring(dimensions));
|
|
}
|
|
|
|
/**
|
|
* Get the element type of an array type.
|
|
*
|
|
* @return The element type.
|
|
* @exception IllegalArgumentException
|
|
* If the type is not an array.
|
|
*/
|
|
public Type elementType() {
|
|
return elementType(1);
|
|
}
|
|
|
|
/**
|
|
* Get a return type of a method type.
|
|
*
|
|
* @return The return type.
|
|
*/
|
|
public Type returnType() {
|
|
return this.returnType;
|
|
}
|
|
|
|
/**
|
|
* If this Type is a method type, get the parameter types of the method,
|
|
* including empty positions for the second word of wide types. This method
|
|
* is good for figuring out which local variables go with parameters. Recall
|
|
* that wide data takes up two local variables.
|
|
*/
|
|
public Type[] indexedParamTypes() {
|
|
if (paramTypes == null) {
|
|
return null;
|
|
}
|
|
|
|
final ArrayList p = new ArrayList(paramTypes.length * 2);
|
|
|
|
// Count longs and doubles as 2.
|
|
for (int i = 0; i < paramTypes.length; i++) {
|
|
p.add(paramTypes[i]);
|
|
if (paramTypes[i].isWide()) {
|
|
p.add(null);
|
|
}
|
|
}
|
|
|
|
final Object[] a = p.toArray();
|
|
final Type[] types = new Type[a.length];
|
|
System.arraycopy(a, 0, types, 0, a.length);
|
|
return types;
|
|
}
|
|
|
|
/**
|
|
* Get the parameter types of the method, not including empty positions for
|
|
* the second word of wide types.
|
|
*
|
|
* @return The parameter types.
|
|
*/
|
|
public Type[] paramTypes() {
|
|
return this.paramTypes;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of slots in the stack that this Type takes up on the
|
|
* JVM stack. This type descriptor is intended to represent the parameters
|
|
* of a method. If there are no parameters, 0 is returned. Else return count
|
|
* each parameter as 1 except wide parameters (longs and doubles) as 2.
|
|
* <p>
|
|
* It also give you an idea of how may parameters a method has.
|
|
*/
|
|
public int stackHeight() {
|
|
if (isVoid()) {
|
|
return 0;
|
|
}
|
|
|
|
if (isWide()) {
|
|
return 2;
|
|
}
|
|
|
|
if (paramTypes != null) {
|
|
int numParams = 0;
|
|
|
|
// Count longs and doubles as 2.
|
|
for (int i = 0; i < paramTypes.length; i++) {
|
|
if (paramTypes[i].isWide()) {
|
|
numParams++;
|
|
}
|
|
numParams++;
|
|
}
|
|
|
|
return numParams;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Hash the type.
|
|
*
|
|
* @return The hash code.
|
|
*/
|
|
public int hashCode() {
|
|
return desc.hashCode();
|
|
}
|
|
|
|
/**
|
|
* Returns <tt>true</tt> if two <tt>Type</tt>s are equal. Equal <tT>Type</tt>s
|
|
* have equal descriptors.
|
|
*/
|
|
public boolean equals(final Object obj) {
|
|
return (obj != null) && (obj instanceof Type)
|
|
&& ((Type) obj).desc.equals(desc);
|
|
}
|
|
|
|
private static Comparator comparator = null;
|
|
|
|
/**
|
|
* Returns a <tt>Comparator</tt> used to compare <tt>Type</tt>s.
|
|
*/
|
|
public static Comparator comparator() {
|
|
if (Type.comparator != null) {
|
|
return (Type.comparator);
|
|
}
|
|
|
|
Type.comparator = new Comparator() {
|
|
public int compare(final Object o1, final Object o2) {
|
|
// o1 < o2 : -1
|
|
// o1 == o2 : 0
|
|
// o1 > o2 : 1
|
|
|
|
if (!(o1 instanceof Type)) {
|
|
throw new IllegalArgumentException(o1 + " not a Type");
|
|
}
|
|
|
|
if (!(o2 instanceof Type)) {
|
|
throw new IllegalArgumentException(o2 + " not a Type");
|
|
}
|
|
|
|
final Type t1 = (Type) o1;
|
|
final Type t2 = (Type) o2;
|
|
|
|
final String d1 = t1.descriptor();
|
|
final String d2 = t2.descriptor();
|
|
|
|
return (d1.compareToIgnoreCase(d2));
|
|
|
|
}
|
|
|
|
public boolean equals(final Object o) {
|
|
if (o == this) {
|
|
return (true);
|
|
} else {
|
|
return (false);
|
|
}
|
|
}
|
|
};
|
|
return (Type.comparator);
|
|
}
|
|
|
|
private static final Comparator printComparator = null;
|
|
|
|
/**
|
|
* Returns a <tt>Comparator</tt> that compares <tt>Type</tt>s based on
|
|
* how they are displayed.
|
|
*/
|
|
public static Comparator printComparator() {
|
|
if (Type.printComparator != null) {
|
|
return (Type.printComparator);
|
|
}
|
|
|
|
Type.comparator = new Comparator() {
|
|
public int compare(final Object o1, final Object o2) {
|
|
// o1 < o2 : -1
|
|
// o1 == o2 : 0
|
|
// o1 > o2 : 1
|
|
|
|
if (!(o1 instanceof Type)) {
|
|
throw new IllegalArgumentException(o1 + " not a Type");
|
|
}
|
|
|
|
if (!(o2 instanceof Type)) {
|
|
throw new IllegalArgumentException(o2 + " not a Type");
|
|
}
|
|
|
|
final Type t1 = (Type) o1;
|
|
final Type t2 = (Type) o2;
|
|
|
|
final String d1 = t1.toString();
|
|
final String d2 = t2.toString();
|
|
|
|
return (d1.compareToIgnoreCase(d2));
|
|
|
|
}
|
|
|
|
public boolean equals(final Object o) {
|
|
if (o == this) {
|
|
return (true);
|
|
} else {
|
|
return (false);
|
|
}
|
|
}
|
|
};
|
|
return (Type.comparator);
|
|
}
|
|
|
|
/**
|
|
* Returns a string representing the truncated name of a <tt>Type</tt>.
|
|
* For instance <tt>java/lang/String</tt> would just return
|
|
* <tt>String</tt>.
|
|
*/
|
|
public static String truncatedName(final Type type) {
|
|
// Sometimes type can be null! I don't know why.
|
|
if (type == null) {
|
|
return ("");
|
|
}
|
|
|
|
// System.out.println("Truncating type: " + type.desc);
|
|
|
|
if (type.isPrimitive() || type.isVoid() || type.isNull()) {
|
|
return (type.desc); // Don't invoke toString!
|
|
}
|
|
|
|
if (type.isArray()) {
|
|
return ("[" + Type.truncatedName(type.elementType()));
|
|
}
|
|
|
|
if (type.isMethod()) {
|
|
final StringBuffer sb = new StringBuffer();
|
|
sb.append('(');
|
|
final Type[] params = type.indexedParamTypes();
|
|
for (int i = 0; i < params.length; i++) {
|
|
sb.append(Type.truncatedName(params[i]));
|
|
if (i < params.length - 1) {
|
|
sb.append(',');
|
|
}
|
|
}
|
|
sb.append(')');
|
|
sb.append(Type.truncatedName(type.returnType()));
|
|
return (sb.toString());
|
|
}
|
|
|
|
// Otherwise its a normal type such as java/lang/String.
|
|
// First, chop off the package name.
|
|
final String longName = type.className();
|
|
int lastSlash = longName.lastIndexOf('/');
|
|
if (lastSlash == -1) {
|
|
lastSlash = longName.lastIndexOf('.');
|
|
}
|
|
final String className = longName.substring(lastSlash + 1);
|
|
|
|
if (className.length() > 8) {
|
|
// Come up with an abbreviated name. Find each capital letter
|
|
// in the name. If there are fewer than 8 capital letters, then
|
|
// use characters after the last capital for a total of 8
|
|
// characters. For instance, StringBuffer becomes SBuffer.
|
|
|
|
// Count capitals
|
|
final StringBuffer caps = new StringBuffer();
|
|
final int nameLength = className.length();
|
|
for (int i = 0; i < nameLength; i++) {
|
|
final char c = className.charAt(i);
|
|
if (Character.isUpperCase(c)) {
|
|
caps.append(c);
|
|
}
|
|
}
|
|
|
|
if (caps.length() > 8) {
|
|
// This is an UGLY class name. Use the first four caps and
|
|
// the last four caps.
|
|
final int capsLength = caps.length();
|
|
for (int i = 4; i < capsLength - 4; i++) {
|
|
caps.deleteCharAt(i);
|
|
}
|
|
return (caps.toString());
|
|
}
|
|
|
|
if (caps.length() <= 0) {
|
|
// Grumble. No caps in name. Return first 8 characters
|
|
return (className.substring(0, 8));
|
|
}
|
|
|
|
final char lastCap = caps.charAt(caps.length() - 1);
|
|
final int indexLastCap = className.lastIndexOf(lastCap);
|
|
final int capsLength = caps.length();
|
|
int end;
|
|
|
|
if (capsLength + (nameLength - indexLastCap) > 8) {
|
|
end = indexLastCap + 1 + (8 - capsLength);
|
|
} else {
|
|
end = nameLength;
|
|
}
|
|
|
|
final String endOfName = className.substring(indexLastCap + 1, end);
|
|
|
|
caps.append(endOfName);
|
|
return (caps.toString());
|
|
}
|
|
|
|
// Just return name of class
|
|
return (className);
|
|
|
|
}
|
|
|
|
/**
|
|
* Convert the type to a string.
|
|
*
|
|
* @return A string representation of the type.
|
|
*/
|
|
public String toString() {
|
|
if (Type.PRINT_TRUNCATED) {
|
|
return (Type.truncatedName(this));
|
|
} else {
|
|
return desc;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test truncatedName.
|
|
*/
|
|
public static void main(final String args[]) {
|
|
Type.truncatedName(Type.getType("(D)V"));
|
|
|
|
// Print the truncated name of each argument
|
|
for (int i = 0; i < args.length; i++) {
|
|
System.out.println("Truncated name of " + args[i] + ": "
|
|
+ Type.truncatedName(Type.getType(args[i])));
|
|
|
|
}
|
|
}
|
|
}
|
|
|