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.
 
 
 
 
bloat/src/EDU/purdue/cs/bloat/editor/Type.java

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])));
}
}
}