/* * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. * 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. */ package sun.tools.java; import java.util.Hashtable; /** * This class represents an Java Type.
* * It encapsulates an Java type signature and it provides * quick access to the components of the type. Note that * all types are hashed into a hashtable (typeHash), that * means that each distinct type is only allocated once, * saving space and making equality checks cheap.
* * For simple types use the constants defined in this class. * (Type.tInt, Type.tShort, ...). To create complex types use * the static methods Type.tArray, Type.tMethod or Type.tClass. * * For classes, arrays and method types a sub class of class * type is created which defines the extra type components. * * WARNING: The contents of this source file are not part of any * supported API. Code that depends on them does so at its own risk: * they are subject to change or removal without notice. * * @see ArrayType * @see ClassType * @see MethodType * @author Arthur van Hoff */ public class Type implements Constants { /** * This hashtable is used to cache types */ private static final Hashtable typeHash = new Hashtable(231); /** * The TypeCode of this type. The value of this field is one * of the TC_* contant values defined in Constants. * @see Constants */ protected int typeCode; /** * The TypeSignature of this type. This type signature is * equivalent to the runtime type signatures used by the * interpreter. */ protected String typeSig; /* * Predefined types. */ public static final Type noArgs[] = new Type[0]; public static final Type tError = new Type(TC_ERROR, "?"); public static final Type tPackage = new Type(TC_ERROR, "."); public static final Type tNull = new Type(TC_NULL, "*"); public static final Type tVoid = new Type(TC_VOID, SIG_VOID); public static final Type tBoolean = new Type(TC_BOOLEAN, SIG_BOOLEAN); public static final Type tByte = new Type(TC_BYTE, SIG_BYTE); public static final Type tChar = new Type(TC_CHAR, SIG_CHAR); public static final Type tShort = new Type(TC_SHORT, SIG_SHORT); public static final Type tInt = new Type(TC_INT, SIG_INT); public static final Type tFloat = new Type(TC_FLOAT, SIG_FLOAT); public static final Type tLong = new Type(TC_LONG, SIG_LONG); public static final Type tDouble = new Type(TC_DOUBLE, SIG_DOUBLE); public static final Type tObject = Type.tClass(idJavaLangObject); public static final Type tClassDesc = Type.tClass(idJavaLangClass); public static final Type tString = Type.tClass(idJavaLangString); public static final Type tCloneable = Type.tClass(idJavaLangCloneable); public static final Type tSerializable = Type.tClass(idJavaIoSerializable); /** * Create a type given a typecode and a type signature. */ protected Type(int typeCode, String typeSig) { this.typeCode = typeCode; this.typeSig = typeSig; typeHash.put(typeSig, this); } /** * Return the Java type signature. */ public final String getTypeSignature() { return typeSig; } /** * Return the type code. */ public final int getTypeCode() { return typeCode; } /** * Return the type mask. The bits in this mask correspond * to the TM_* constants defined in Constants. Only one bit * is set at a type. * @see Constants */ public final int getTypeMask() { return 1 << typeCode; } /** * Check for a certain type. */ public final boolean isType(int tc) { return typeCode == tc; } /** * Check to see if this is the bogus type "array of void" * * Although this highly degenerate "type" is not constructable from * the grammar, the Parser accepts it. Rather than monkey with the * Parser, we check for the bogus type at specific points and give * a nice error. */ public boolean isVoidArray() { // a void type is not a void array. if (!isType(TC_ARRAY)) { return false; } // If this is an array, find out what its element type is. Type type = this; while (type.isType(TC_ARRAY)) type = type.getElementType(); return type.isType(TC_VOID); } /** * Check for a certain set of types. */ public final boolean inMask(int tm) { return ((1 << typeCode) & tm) != 0; } /** * Create an array type. */ public static synchronized Type tArray(Type elem) { String sig = new String(SIG_ARRAY + elem.getTypeSignature()); Type t = (Type)typeHash.get(sig); if (t == null) { t = new ArrayType(sig, elem); } return t; } /** * Return the element type of an array type. Only works * for array types. */ public Type getElementType() { throw new CompilerError("getElementType"); } /** * Return the array dimension. Only works for * array types. */ public int getArrayDimension() { return 0; } /** * Create a class type. * @arg className the fully qualified class name */ public static synchronized Type tClass(Identifier className) { if (className.isInner()) { Type t = tClass(mangleInnerType(className)); if (t.getClassName() != className) // Somebody got here first with a mangled name. // (Perhaps it came from a binary.) changeClassName(t.getClassName(), className); return t; } // see if we've cached the object in the Identifier if (className.typeObject != null) { return className.typeObject; } String sig = new String(SIG_CLASS + className.toString().replace('.', SIGC_PACKAGE) + SIG_ENDCLASS); Type t = (Type)typeHash.get(sig); if (t == null) { t = new ClassType(sig, className); } className.typeObject = t; // cache the Type object in the Identifier return t; } /** * Return the ClassName. Only works on class types. */ public Identifier getClassName() { throw new CompilerError("getClassName:" + this); } /** * Given an inner identifier, return the non-inner, mangled * representation used to manage signatures. * * Note: It is changed to 'public' for Jcov file generation. * (see Assembler.java) */ public static Identifier mangleInnerType(Identifier className) { // Map "pkg.Foo. Bar" to "pkg.Foo$Bar". if (!className.isInner()) return className; Identifier mname = Identifier.lookup( className.getFlatName().toString(). replace('.', SIGC_INNERCLASS) ); if (mname.isInner()) throw new CompilerError("mangle "+mname); return Identifier.lookup(className.getQualifier(), mname); } /** * We have learned that a signature means something other * that what we thought it meant. Live with it: Change all * affected data structures to reflect the new name of the old type. *
* (This is necessary because of an ambiguity between the
* low-level signatures of inner types and their manglings.
* Note that the latter are also valid class names.)
*/
static void changeClassName(Identifier oldName, Identifier newName) {
// Note: If we are upgrading "pkg.Foo$Bar" to "pkg.Foo. Bar",
// we assume someone else will come along and deal with any types
// inner within Bar. So, there's only one change to make.
((ClassType)Type.tClass(oldName)).className = newName;
}
/**
* Create a method type with no arguments.
*/
public static synchronized Type tMethod(Type ret) {
return tMethod(ret, noArgs);
}
/**
* Create a method type with arguments.
*/
public static synchronized Type tMethod(Type returnType, Type argTypes[]) {
StringBuffer buf = new StringBuffer();
buf.append(SIG_METHOD);
for (int i = 0 ; i < argTypes.length ; i++) {
buf.append(argTypes[i].getTypeSignature());
}
buf.append(SIG_ENDMETHOD);
buf.append(returnType.getTypeSignature());
String sig = buf.toString();
Type t = (Type)typeHash.get(sig);
if (t == null) {
t = new MethodType(sig, returnType, argTypes);
}
return t;
}
/**
* Return the return type. Only works for method types.
*/
public Type getReturnType() {
throw new CompilerError("getReturnType");
}
/**
* Return the argument types. Only works for method types.
*/
public Type getArgumentTypes()[] {
throw new CompilerError("getArgumentTypes");
}
/**
* Create a Type from an Java type signature.
* @exception CompilerError invalid type signature.
*/
public static synchronized Type tType(String sig) {
Type t = (Type)typeHash.get(sig);
if (t != null) {
return t;
}
switch (sig.charAt(0)) {
case SIGC_ARRAY:
return Type.tArray(tType(sig.substring(1)));
case SIGC_CLASS:
return Type.tClass(Identifier.lookup(sig.substring(1, sig.length() - 1).replace(SIGC_PACKAGE, '.')));
case SIGC_METHOD: {
Type argv[] = new Type[8];
int argc = 0;
int i, j;
for (i = 1 ; sig.charAt(i) != SIGC_ENDMETHOD ; i = j) {
for (j = i ; sig.charAt(j) == SIGC_ARRAY ; j++);
if (sig.charAt(j++) == SIGC_CLASS) {
while (sig.charAt(j++) != SIGC_ENDCLASS);
}
if (argc == argv.length) {
Type newargv[] = new Type[argc * 2];
System.arraycopy(argv, 0, newargv, 0, argc);
argv = newargv;
}
argv[argc++] = tType(sig.substring(i, j));
}
Type argtypes[] = new Type[argc];
System.arraycopy(argv, 0, argtypes, 0, argc);
return Type.tMethod(tType(sig.substring(i + 1)), argtypes);
}
}
throw new CompilerError("invalid TypeSignature:" + sig);
}
/**
* Check if the type arguments are the same.
* @return true if both types are method types and the
* argument types are identical.
*/
public boolean equalArguments(Type t) {
return false;
}
/**
* Return the amount of space this type takes up on the
* Java operand stack. For a method this is equal to the
* total space taken up by the arguments.
*/
public int stackSize() {
switch (typeCode) {
case TC_ERROR:
case TC_VOID:
return 0;
case TC_BOOLEAN:
case TC_BYTE:
case TC_SHORT:
case TC_CHAR:
case TC_INT:
case TC_FLOAT:
case TC_ARRAY:
case TC_CLASS:
return 1;
case TC_LONG:
case TC_DOUBLE:
return 2;
}
throw new CompilerError("stackSize " + toString());
}
/**
* Return the type code offset. This offset can be added to
* an opcode to get the right opcode type. Most opcodes
* are ordered: int, long, float, double, array. For
* example: iload, lload fload, dload, aload. So the
* appropriate opcode is iadd + type.getTypeCodeOffset().
*/
public int getTypeCodeOffset() {
switch (typeCode) {
case TC_BOOLEAN:
case TC_BYTE:
case TC_SHORT:
case TC_CHAR:
case TC_INT:
return 0;
case TC_LONG:
return 1;
case TC_FLOAT:
return 2;
case TC_DOUBLE:
return 3;
case TC_NULL:
case TC_ARRAY:
case TC_CLASS:
return 4;
}
throw new CompilerError("invalid typecode: " + typeCode);
}
/**
* Convert a Type to a string, if abbrev is true class names are
* not fully qualified, if ret is true the return type is included.
*/
public String typeString(String id, boolean abbrev, boolean ret) {
String s = null;
switch (typeCode) {
case TC_NULL: s = "null"; break;
case TC_VOID: s = "void"; break;
case TC_BOOLEAN: s = "boolean"; break;
case TC_BYTE: s = "byte"; break;
case TC_CHAR: s = "char"; break;
case TC_SHORT: s = "short"; break;
case TC_INT: s = "int"; break;
case TC_LONG: s = "long"; break;
case TC_FLOAT: s = "float"; break;
case TC_DOUBLE: s = "double"; break;
case TC_ERROR: s = "