/* * Copyright (c) 1994, 2019, 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 = 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 = 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[]) { StringBuilder sb = new StringBuilder(); sb.append(SIG_METHOD); for (int i = 0 ; i < argTypes.length ; i++) { sb.append(argTypes[i].getTypeSignature()); } sb.append(SIG_ENDMETHOD); sb.append(returnType.getTypeSignature()); String sig = sb.toString(); Type t = 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 = 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 = ""; if (this==tPackage) s = ""; break; default: s = "unknown"; } return (id.length() > 0) ? s + " " + id : s; } /** * Create a type string, given an identifier. */ public String typeString(String id) { return typeString(id, false, true); } /** * Convert to a String */ public String toString() { return typeString("", false, true); } }