| /* |
| * 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.<p> |
| * |
| * 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.<p> |
| * |
| * 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<String, Type> 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. |
| * <p> |
| * (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 = "<error>"; |
| if (this==tPackage) s = "<package>"; |
| 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); |
| } |
| } |