1/*
2 * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.tools.java;
27
28import java.util.Hashtable;
29
30/**
31 * This class represents an Java Type.<p>
32 *
33 * It encapsulates an Java type signature and it provides
34 * quick access to the components of the type. Note that
35 * all types are hashed into a hashtable (typeHash), that
36 * means that each distinct type is only allocated once,
37 * saving space and making equality checks cheap.<p>
38 *
39 * For simple types use the constants defined in this class.
40 * (Type.tInt, Type.tShort, ...). To create complex types use
41 * the static methods Type.tArray, Type.tMethod or Type.tClass.
42 *
43 * For classes, arrays and method types a sub class of class
44 * type is created which defines the extra type components.
45 *
46 * WARNING: The contents of this source file are not part of any
47 * supported API.  Code that depends on them does so at its own risk:
48 * they are subject to change or removal without notice.
49 *
50 * @see         ArrayType
51 * @see         ClassType
52 * @see         MethodType
53 * @author      Arthur van Hoff
54 */
55public
56class Type implements Constants {
57    /**
58     * This hashtable is used to cache types
59     */
60    private static final Hashtable<String, Type> typeHash = new Hashtable<>(231);
61
62    /**
63     * The TypeCode of this type. The value of this field is one
64     * of the TC_* contant values defined in Constants.
65     * @see Constants
66     */
67    protected int typeCode;
68
69    /**
70     * The TypeSignature of this type. This type signature is
71     * equivalent to the runtime type signatures used by the
72     * interpreter.
73     */
74    protected String typeSig;
75
76    /*
77     * Predefined types.
78     */
79    public static final Type noArgs[]   = new Type[0];
80    public static final Type tError     = new Type(TC_ERROR,    "?");
81    public static final Type tPackage   = new Type(TC_ERROR,    ".");
82    public static final Type tNull      = new Type(TC_NULL,     "*");
83    public static final Type tVoid      = new Type(TC_VOID,     SIG_VOID);
84    public static final Type tBoolean   = new Type(TC_BOOLEAN,  SIG_BOOLEAN);
85    public static final Type tByte      = new Type(TC_BYTE,     SIG_BYTE);
86    public static final Type tChar      = new Type(TC_CHAR,     SIG_CHAR);
87    public static final Type tShort     = new Type(TC_SHORT,    SIG_SHORT);
88    public static final Type tInt       = new Type(TC_INT,      SIG_INT);
89    public static final Type tFloat     = new Type(TC_FLOAT,    SIG_FLOAT);
90    public static final Type tLong      = new Type(TC_LONG,     SIG_LONG);
91    public static final Type tDouble    = new Type(TC_DOUBLE,   SIG_DOUBLE);
92    public static final Type tObject    = Type.tClass(idJavaLangObject);
93    public static final Type tClassDesc = Type.tClass(idJavaLangClass);
94    public static final Type tString    = Type.tClass(idJavaLangString);
95    public static final Type tCloneable = Type.tClass(idJavaLangCloneable);
96    public static final Type tSerializable = Type.tClass(idJavaIoSerializable);
97
98    /**
99     * Create a type given a typecode and a type signature.
100     */
101    protected Type(int typeCode, String typeSig) {
102        this.typeCode = typeCode;
103        this.typeSig = typeSig;
104        typeHash.put(typeSig, this);
105    }
106
107    /**
108     * Return the Java type signature.
109     */
110    public final String getTypeSignature() {
111        return typeSig;
112    }
113
114    /**
115     * Return the type code.
116     */
117    public final int getTypeCode() {
118        return typeCode;
119    }
120
121    /**
122     * Return the type mask. The bits in this mask correspond
123     * to the TM_* constants defined in Constants. Only one bit
124     * is set at a type.
125     * @see Constants
126     */
127    public final int getTypeMask() {
128        return 1 << typeCode;
129    }
130
131    /**
132     * Check for a certain type.
133     */
134    public final boolean isType(int tc) {
135        return typeCode == tc;
136    }
137
138    /**
139     * Check to see if this is the bogus type "array of void"
140     *
141     * Although this highly degenerate "type" is not constructable from
142     * the grammar, the Parser accepts it.  Rather than monkey with the
143     * Parser, we check for the bogus type at specific points and give
144     * a nice error.
145     */
146    public boolean isVoidArray() {
147        // a void type is not a void array.
148        if (!isType(TC_ARRAY)) {
149            return false;
150        }
151        // If this is an array, find out what its element type is.
152        Type type = this;
153        while (type.isType(TC_ARRAY))
154            type = type.getElementType();
155
156        return type.isType(TC_VOID);
157    }
158
159
160    /**
161     * Check for a certain set of types.
162     */
163    public final boolean inMask(int tm) {
164        return ((1 << typeCode) & tm) != 0;
165    }
166
167    /**
168     * Create an array type.
169     */
170    public static synchronized Type tArray(Type elem) {
171        String sig = new String(SIG_ARRAY + elem.getTypeSignature());
172        Type t = typeHash.get(sig);
173        if (t == null) {
174            t = new ArrayType(sig, elem);
175        }
176        return t;
177    }
178
179    /**
180     * Return the element type of an array type. Only works
181     * for array types.
182     */
183    public Type getElementType() {
184        throw new CompilerError("getElementType");
185    }
186
187    /**
188     * Return the array dimension. Only works for
189     * array types.
190     */
191    public int getArrayDimension() {
192        return 0;
193    }
194
195    /**
196     * Create a class type.
197     * @arg className the fully qualified class name
198     */
199    public static synchronized Type tClass(Identifier className) {
200        if (className.isInner()) {
201            Type t = tClass(mangleInnerType(className));
202            if (t.getClassName() != className)
203                // Somebody got here first with a mangled name.
204                // (Perhaps it came from a binary.)
205                changeClassName(t.getClassName(), className);
206            return t;
207        }
208        // see if we've cached the object in the Identifier
209        if (className.typeObject != null) {
210            return className.typeObject;
211        }
212        String sig =
213            new String(SIG_CLASS +
214                       className.toString().replace('.', SIGC_PACKAGE) +
215                       SIG_ENDCLASS);
216        Type t = typeHash.get(sig);
217        if (t == null) {
218            t = new ClassType(sig, className);
219        }
220
221        className.typeObject = t; // cache the Type object in the Identifier
222        return t;
223    }
224
225    /**
226     * Return the ClassName. Only works on class types.
227     */
228    public Identifier getClassName() {
229        throw new CompilerError("getClassName:" + this);
230    }
231
232    /**
233     * Given an inner identifier, return the non-inner, mangled
234     * representation used to manage signatures.
235     *
236     * Note: It is changed to 'public' for Jcov file generation.
237     * (see Assembler.java)
238     */
239
240    public static Identifier mangleInnerType(Identifier className) {
241        // Map "pkg.Foo. Bar" to "pkg.Foo$Bar".
242        if (!className.isInner())  return className;
243        Identifier mname = Identifier.lookup(
244                                className.getFlatName().toString().
245                                replace('.', SIGC_INNERCLASS) );
246        if (mname.isInner())  throw new CompilerError("mangle "+mname);
247        return Identifier.lookup(className.getQualifier(), mname);
248    }
249
250    /**
251     * We have learned that a signature means something other
252     * that what we thought it meant.  Live with it:  Change all
253     * affected data structures to reflect the new name of the old type.
254     * <p>
255     * (This is necessary because of an ambiguity between the
256     * low-level signatures of inner types and their manglings.
257     * Note that the latter are also valid class names.)
258     */
259    static void changeClassName(Identifier oldName, Identifier newName) {
260        // Note:  If we are upgrading "pkg.Foo$Bar" to "pkg.Foo. Bar",
261        // we assume someone else will come along and deal with any types
262        // inner within Bar.  So, there's only one change to make.
263        ((ClassType)Type.tClass(oldName)).className = newName;
264    }
265
266    /**
267     * Create a method type with no arguments.
268     */
269    public static synchronized Type tMethod(Type ret) {
270        return tMethod(ret, noArgs);
271    }
272
273    /**
274     * Create a method type with arguments.
275     */
276    public static synchronized Type tMethod(Type returnType, Type argTypes[]) {
277        StringBuilder sb = new StringBuilder();
278        sb.append(SIG_METHOD);
279        for (int i = 0 ; i < argTypes.length ; i++) {
280            sb.append(argTypes[i].getTypeSignature());
281        }
282        sb.append(SIG_ENDMETHOD);
283        sb.append(returnType.getTypeSignature());
284
285        String sig = sb.toString();
286        Type t = typeHash.get(sig);
287        if (t == null) {
288            t = new MethodType(sig, returnType, argTypes);
289        }
290        return t;
291    }
292
293    /**
294     * Return the return type. Only works for method types.
295     */
296    public Type getReturnType() {
297        throw new CompilerError("getReturnType");
298    }
299
300    /**
301     * Return the argument types. Only works for method types.
302     */
303    public Type getArgumentTypes()[] {
304        throw new CompilerError("getArgumentTypes");
305    }
306
307    /**
308     * Create a Type from an Java type signature.
309     * @exception CompilerError invalid type signature.
310     */
311    public static synchronized Type tType(String sig) {
312        Type t = typeHash.get(sig);
313        if (t != null) {
314            return t;
315        }
316
317        switch (sig.charAt(0)) {
318          case SIGC_ARRAY:
319            return Type.tArray(tType(sig.substring(1)));
320
321          case SIGC_CLASS:
322            return Type.tClass(Identifier.lookup(sig.substring(1, sig.length() - 1).replace(SIGC_PACKAGE, '.')));
323
324          case SIGC_METHOD: {
325            Type argv[] = new Type[8];
326            int argc = 0;
327            int i, j;
328
329            for (i = 1 ; sig.charAt(i) != SIGC_ENDMETHOD ; i = j) {
330                for (j = i ; sig.charAt(j) == SIGC_ARRAY ; j++);
331                if (sig.charAt(j++) == SIGC_CLASS) {
332                    while (sig.charAt(j++) != SIGC_ENDCLASS);
333                }
334                if (argc == argv.length) {
335                    Type newargv[] = new Type[argc * 2];
336                    System.arraycopy(argv, 0, newargv, 0, argc);
337                    argv = newargv;
338                }
339                argv[argc++] = tType(sig.substring(i, j));
340            }
341
342            Type argtypes[] = new Type[argc];
343            System.arraycopy(argv, 0, argtypes, 0, argc);
344            return Type.tMethod(tType(sig.substring(i + 1)), argtypes);
345          }
346        }
347
348        throw new CompilerError("invalid TypeSignature:" + sig);
349    }
350
351    /**
352     * Check if the type arguments are the same.
353     * @return true if both types are method types and the
354     * argument types are identical.
355     */
356    public boolean equalArguments(Type t) {
357        return false;
358    }
359
360    /**
361     * Return the amount of space this type takes up on the
362     * Java operand stack. For a method this is equal to the
363     * total space taken up by the arguments.
364     */
365    public int stackSize() {
366        switch (typeCode) {
367          case TC_ERROR:
368          case TC_VOID:
369            return 0;
370          case TC_BOOLEAN:
371          case TC_BYTE:
372          case TC_SHORT:
373          case TC_CHAR:
374          case TC_INT:
375          case TC_FLOAT:
376          case TC_ARRAY:
377          case TC_CLASS:
378            return 1;
379          case TC_LONG:
380          case TC_DOUBLE:
381            return 2;
382        }
383        throw new CompilerError("stackSize " + toString());
384    }
385
386    /**
387     * Return the type code offset. This offset can be added to
388     * an opcode to get the right opcode type. Most opcodes
389     * are ordered: int, long, float, double, array. For
390     * example: iload, lload fload, dload, aload. So the
391     * appropriate opcode is iadd + type.getTypeCodeOffset().
392     */
393    public int getTypeCodeOffset() {
394        switch (typeCode) {
395          case TC_BOOLEAN:
396          case TC_BYTE:
397          case TC_SHORT:
398          case TC_CHAR:
399          case TC_INT:
400            return 0;
401          case TC_LONG:
402            return 1;
403          case TC_FLOAT:
404            return 2;
405          case TC_DOUBLE:
406            return 3;
407          case TC_NULL:
408          case TC_ARRAY:
409          case TC_CLASS:
410            return 4;
411        }
412        throw new CompilerError("invalid typecode: " + typeCode);
413    }
414
415    /**
416     * Convert a Type to a string, if abbrev is true class names are
417     * not fully qualified, if ret is true the return type is included.
418     */
419    public String typeString(String id, boolean abbrev, boolean ret) {
420        String s = null;
421
422        switch (typeCode) {
423          case TC_NULL:         s = "null";    break;
424          case TC_VOID:         s = "void";    break;
425          case TC_BOOLEAN:      s = "boolean"; break;
426          case TC_BYTE:         s = "byte";    break;
427          case TC_CHAR:         s = "char";    break;
428          case TC_SHORT:        s = "short";   break;
429          case TC_INT:          s = "int";     break;
430          case TC_LONG:         s = "long";    break;
431          case TC_FLOAT:        s = "float";   break;
432          case TC_DOUBLE:       s = "double";  break;
433          case TC_ERROR:        s = "<error>";
434                                if (this==tPackage) s = "<package>";
435                                break;
436          default:              s = "unknown";
437          }
438
439        return (id.length() > 0) ? s + " " + id : s;
440    }
441
442    /**
443     * Create a type string, given an identifier.
444     */
445    public String typeString(String id) {
446        return typeString(id, false, true);
447    }
448
449    /**
450     * Convert to a String
451     */
452    public String toString() {
453        return typeString("", false, true);
454    }
455}
456