1/*
2 * Copyright (c) 2010, 2016, 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 jdk.nashorn.internal.codegen.types;
27
28import static jdk.internal.org.objectweb.asm.Opcodes.DALOAD;
29import static jdk.internal.org.objectweb.asm.Opcodes.DASTORE;
30import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
31import static jdk.internal.org.objectweb.asm.Opcodes.DUP2;
32import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X1;
33import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X2;
34import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X1;
35import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X2;
36import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
37import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
38import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
39import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
40import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
41import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
42import static jdk.internal.org.objectweb.asm.Opcodes.POP;
43import static jdk.internal.org.objectweb.asm.Opcodes.POP2;
44import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
45import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
46import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
47import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
48
49import java.io.DataInput;
50import java.io.DataOutput;
51import java.io.IOException;
52import java.io.Serializable;
53import java.util.Collections;
54import java.util.Map;
55import java.util.TreeMap;
56import java.util.WeakHashMap;
57import java.util.concurrent.ConcurrentHashMap;
58import java.util.concurrent.ConcurrentMap;
59import jdk.internal.org.objectweb.asm.MethodVisitor;
60import jdk.nashorn.internal.codegen.CompilerConstants.Call;
61import jdk.nashorn.internal.runtime.Context;
62import jdk.nashorn.internal.runtime.ScriptObject;
63import jdk.nashorn.internal.runtime.Undefined;
64
65/**
66 * This is the representation of a JavaScript type, disassociated from java
67 * Classes, with the basis for conversion weight, mapping to ASM types
68 * and implementing the ByteCodeOps interface which tells this type
69 * how to generate code for various operations.
70 *
71 * Except for ClassEmitter, this is the only class that has to know
72 * about the underlying byte code generation system.
73 *
74 * The different types know how to generate bytecode for the different
75 * operations, inherited from BytecodeOps, that they support. This avoids
76 * if/else chains depending on type in several cases and allows for
77 * more readable and shorter code
78 *
79 * The Type class also contains logic used by the type inference and
80 * for comparing types against each other, as well as the concepts
81 * of narrower to wider types. The widest type is an object. Ideally we
82 * would like as narrow types as possible for code to be efficient, e.g
83 * INTs rather than OBJECTs
84 */
85
86public abstract class Type implements Comparable<Type>, BytecodeOps, Serializable {
87    private static final long serialVersionUID = 1L;
88
89    /** Human readable name for type */
90    private transient final String name;
91
92    /** Descriptor for type */
93    private transient final String descriptor;
94
95    /** The "weight" of the type. Used for picking widest/least specific common type */
96    private transient final int weight;
97
98    /** How many bytecode slots does this type occupy */
99    private transient final int slots;
100
101    /** The class for this type */
102    private final Class<?> clazz;
103
104    /**
105     * Cache for internal types - this is a query that requires complex stringbuilding inside
106     * ASM and it saves startup time to cache the type mappings
107     */
108    private static final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> INTERNAL_TYPE_CACHE =
109            Collections.synchronizedMap(new WeakHashMap<Class<?>, jdk.internal.org.objectweb.asm.Type>());
110
111    /** Internal ASM type for this Type - computed once at construction */
112    private transient final jdk.internal.org.objectweb.asm.Type internalType;
113
114    /** Weights are used to decide which types are "wider" than other types */
115    protected static final int MIN_WEIGHT = -1;
116
117    /** Set way below Integer.MAX_VALUE to prevent overflow when adding weights. Objects are still heaviest. */
118    protected static final int MAX_WEIGHT = 20;
119
120    /**
121     * Constructor
122     *
123     * @param clazz       class for type
124     * @param weight      weight - higher is more generic
125     * @param slots       how many bytecode slots the type takes up
126     */
127    Type(final String name, final Class<?> clazz, final int weight, final int slots) {
128        this.name         = name;
129        this.clazz        = clazz;
130        this.descriptor   = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
131        this.weight       = weight;
132        assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
133        this.slots        = slots;
134        this.internalType = getInternalType(clazz);
135    }
136
137    /**
138     * Get the weight of this type - use this e.g. for sorting method descriptors
139     * @return the weight
140     */
141    public int getWeight() {
142        return weight;
143    }
144
145    /**
146     * Get the Class representing this type
147     * @return the class for this type
148     */
149    public Class<?> getTypeClass() {
150        return clazz;
151    }
152
153    /**
154     * For specialization, return the next, slightly more difficulty, type
155     * to test.
156     *
157     * @return the next Type
158     */
159    public Type nextWider() {
160        return null;
161    }
162
163    /**
164     * Get the boxed type for this class
165     * @return the boxed version of this type or null if N/A
166     */
167    public Class<?> getBoxedType() {
168        assert !getTypeClass().isPrimitive();
169        return null;
170    }
171
172    /**
173     * Returns the character describing the bytecode type for this value on the stack or local variable, identical to
174     * what would be used as the prefix for a bytecode {@code LOAD} or {@code STORE} instruction, therefore it must be
175     * one of {@code A, F, D, I, L}. Also, the special value {@code U} is used for local variable slots that haven't
176     * been initialized yet (it can't appear for a value pushed to the operand stack, those always have known values).
177     * Note that while we allow all JVM internal types, Nashorn doesn't necessarily use them all - currently we don't
178     * have floats, only doubles, but that might change in the future.
179     * @return the character describing the bytecode type for this value on the stack.
180     */
181    public abstract char getBytecodeStackType();
182
183    /**
184     * Generate a method descriptor given a return type and a param array
185     *
186     * @param returnType return type
187     * @param types      parameters
188     *
189     * @return a descriptor string
190     */
191    public static String getMethodDescriptor(final Type returnType, final Type... types) {
192        final jdk.internal.org.objectweb.asm.Type[] itypes = new jdk.internal.org.objectweb.asm.Type[types.length];
193        for (int i = 0; i < types.length; i++) {
194            itypes[i] = types[i].getInternalType();
195        }
196        return jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(returnType.getInternalType(), itypes);
197    }
198
199    /**
200     * Generate a method descriptor given a return type and a param array
201     *
202     * @param returnType return type
203     * @param types      parameters
204     *
205     * @return a descriptor string
206     */
207    public static String getMethodDescriptor(final Class<?> returnType, final Class<?>... types) {
208        final jdk.internal.org.objectweb.asm.Type[] itypes = new jdk.internal.org.objectweb.asm.Type[types.length];
209        for (int i = 0; i < types.length; i++) {
210            itypes[i] = getInternalType(types[i]);
211        }
212        return jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(getInternalType(returnType), itypes);
213    }
214
215    /**
216     * Return a character representing {@code type} in a method signature.
217     *
218     * @param type parameter type
219     * @return descriptor character
220     */
221    public static char getShortSignatureDescriptor(final Type type) {
222        // Use 'Z' for boolean parameters as we need to distinguish from int
223        if (type instanceof BooleanType) {
224            return 'Z';
225        }
226        return type.getBytecodeStackType();
227    }
228
229    /**
230     * Return the type for an internal type, package private - do not use
231     * outside code gen
232     *
233     * @param itype internal type
234     * @return Nashorn type
235     */
236    @SuppressWarnings("fallthrough")
237    private static Type typeFor(final jdk.internal.org.objectweb.asm.Type itype) {
238        switch (itype.getSort()) {
239        case jdk.internal.org.objectweb.asm.Type.BOOLEAN:
240            return BOOLEAN;
241        case jdk.internal.org.objectweb.asm.Type.INT:
242            return INT;
243        case jdk.internal.org.objectweb.asm.Type.LONG:
244            return LONG;
245        case jdk.internal.org.objectweb.asm.Type.DOUBLE:
246            return NUMBER;
247        case jdk.internal.org.objectweb.asm.Type.OBJECT:
248            if (Context.isStructureClass(itype.getClassName())) {
249                return SCRIPT_OBJECT;
250            }
251            return cacheByName.computeIfAbsent(itype.getClassName(), (name) -> {
252                try {
253                    return Type.typeFor(Class.forName(name));
254                } catch(final ClassNotFoundException e) {
255                    throw new AssertionError(e);
256                }
257            });
258        case jdk.internal.org.objectweb.asm.Type.VOID:
259            return null;
260        case jdk.internal.org.objectweb.asm.Type.ARRAY:
261            switch (itype.getElementType().getSort()) {
262            case jdk.internal.org.objectweb.asm.Type.DOUBLE:
263                return NUMBER_ARRAY;
264            case jdk.internal.org.objectweb.asm.Type.INT:
265                return INT_ARRAY;
266            case jdk.internal.org.objectweb.asm.Type.LONG:
267                return LONG_ARRAY;
268            default:
269                assert false;
270            case jdk.internal.org.objectweb.asm.Type.OBJECT:
271                return OBJECT_ARRAY;
272            }
273
274        default:
275            assert false : "Unknown itype : " + itype + " sort " + itype.getSort();
276            break;
277        }
278        return null;
279    }
280
281    /**
282     * Get the return type for a method
283     *
284     * @param methodDescriptor method descriptor
285     * @return return type
286     */
287    public static Type getMethodReturnType(final String methodDescriptor) {
288        return Type.typeFor(jdk.internal.org.objectweb.asm.Type.getReturnType(methodDescriptor));
289    }
290
291    /**
292     * Get type array representing arguments of a method in order
293     *
294     * @param methodDescriptor method descriptor
295     * @return parameter type array
296     */
297    public static Type[] getMethodArguments(final String methodDescriptor) {
298        final jdk.internal.org.objectweb.asm.Type itypes[] = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(methodDescriptor);
299        final Type types[] = new Type[itypes.length];
300        for (int i = 0; i < itypes.length; i++) {
301            types[i] = Type.typeFor(itypes[i]);
302        }
303        return types;
304    }
305
306    /**
307     * Write a map of {@code int} to {@code Type} to an output stream. This is used to store deoptimization state.
308     *
309     * @param typeMap the type map
310     * @param output data output
311     * @throws IOException if write cannot be completed
312     */
313    public static void writeTypeMap(final Map<Integer, Type> typeMap, final DataOutput output) throws IOException {
314        if (typeMap == null) {
315            output.writeInt(0);
316        } else {
317            output.writeInt(typeMap.size());
318            for(final Map.Entry<Integer, Type> e: typeMap.entrySet()) {
319                output.writeInt(e.getKey());
320                final byte typeChar;
321                final Type type = e.getValue();
322                if(type == Type.OBJECT) {
323                    typeChar = 'L';
324                } else if (type == Type.NUMBER) {
325                    typeChar = 'D';
326                } else if (type == Type.LONG) {
327                    typeChar = 'J';
328                } else {
329                    throw new AssertionError();
330                }
331                output.writeByte(typeChar);
332            }
333        }
334    }
335
336    /**
337     * Read a map of {@code int} to {@code Type} from an input stream. This is used to store deoptimization state.
338     *
339     * @param input data input
340     * @return type map
341     * @throws IOException if read cannot be completed
342     */
343    public static Map<Integer, Type> readTypeMap(final DataInput input) throws IOException {
344        final int size = input.readInt();
345        if (size <= 0) {
346            return null;
347        }
348        final Map<Integer, Type> map = new TreeMap<>();
349        for(int i = 0; i < size; ++i) {
350            final int pp = input.readInt();
351            final int typeChar = input.readByte();
352            final Type type;
353            switch (typeChar) {
354                case 'L': type = Type.OBJECT; break;
355                case 'D': type = Type.NUMBER; break;
356                case 'J': type = Type.LONG; break;
357                default: continue;
358            }
359            map.put(pp, type);
360        }
361        return map;
362    }
363
364    static jdk.internal.org.objectweb.asm.Type getInternalType(final String className) {
365        return jdk.internal.org.objectweb.asm.Type.getType(className);
366    }
367
368    private jdk.internal.org.objectweb.asm.Type getInternalType() {
369        return internalType;
370    }
371
372    private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
373        final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> c = INTERNAL_TYPE_CACHE;
374        jdk.internal.org.objectweb.asm.Type itype = c.get(type);
375        if (itype != null) {
376            return itype;
377        }
378        itype = jdk.internal.org.objectweb.asm.Type.getType(type);
379        c.put(type, itype);
380        return itype;
381    }
382
383    private static jdk.internal.org.objectweb.asm.Type getInternalType(final Class<?> type) {
384        return lookupInternalType(type);
385    }
386
387    static void invokestatic(final MethodVisitor method, final Call call) {
388        method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor(), false);
389    }
390
391    /**
392     * Get the internal JVM name of a type
393     * @return the internal name
394     */
395    public String getInternalName() {
396        return jdk.internal.org.objectweb.asm.Type.getInternalName(getTypeClass());
397    }
398
399    /**
400     * Get the internal JVM name of type type represented by a given Java class
401     * @param clazz the class
402     * @return the internal name
403     */
404    public static String getInternalName(final Class<?> clazz) {
405        return jdk.internal.org.objectweb.asm.Type.getInternalName(clazz);
406    }
407
408    /**
409     * Determines whether a type is the UNKNOWN type, i.e. not set yet
410     * Used for type inference.
411     *
412     * @return true if UNKNOWN, false otherwise
413     */
414    public boolean isUnknown() {
415        return this.equals(Type.UNKNOWN);
416    }
417
418    /**
419     * Determines whether this type represents an primitive type according to the ECMAScript specification,
420     * which includes Boolean, Number, and String.
421     *
422     * @return true if a JavaScript primitive type, false otherwise.
423     */
424    public boolean isJSPrimitive() {
425        return !isObject() || isString();
426    }
427
428    /**
429     * Determines whether a type is the BOOLEAN type
430     * @return true if BOOLEAN, false otherwise
431     */
432    public boolean isBoolean() {
433        return this.equals(Type.BOOLEAN);
434    }
435
436    /**
437     * Determines whether a type is the INT type
438     * @return true if INTEGER, false otherwise
439     */
440    public boolean isInteger() {
441        return this.equals(Type.INT);
442    }
443
444    /**
445     * Determines whether a type is the LONG type
446     * @return true if LONG, false otherwise
447     */
448    public boolean isLong() {
449        return this.equals(Type.LONG);
450    }
451
452    /**
453     * Determines whether a type is the NUMBER type
454     * @return true if NUMBER, false otherwise
455     */
456    public boolean isNumber() {
457        return this.equals(Type.NUMBER);
458    }
459
460    /**
461     * Determines whether a type is numeric, i.e. NUMBER,
462     * INT, LONG.
463     *
464     * @return true if numeric, false otherwise
465     */
466    public boolean isNumeric() {
467        return this instanceof NumericType;
468    }
469
470    /**
471     * Determines whether a type is an array type, i.e.
472     * OBJECT_ARRAY or NUMBER_ARRAY (for now)
473     *
474     * @return true if an array type, false otherwise
475     */
476    public boolean isArray() {
477        return this instanceof ArrayType;
478    }
479
480    /**
481     * Determines if a type takes up two bytecode slots or not
482     *
483     * @return true if type takes up two bytecode slots rather than one
484     */
485    public boolean isCategory2() {
486        return getSlots() == 2;
487    }
488
489    /**
490     * Determines whether a type is an OBJECT type, e.g. OBJECT, STRING,
491     * NUMBER_ARRAY etc.
492     *
493     * @return true if object type, false otherwise
494     */
495    public boolean isObject() {
496        return this instanceof ObjectType;
497    }
498
499    /**
500     * Is this a primitive type (e.g int, long, double, boolean)
501     * @return true if primitive
502     */
503    public boolean isPrimitive() {
504        return !isObject();
505    }
506
507    /**
508     * Determines whether a type is a STRING type
509     *
510     * @return true if object type, false otherwise
511     */
512    public boolean isString() {
513        return this.equals(Type.STRING);
514    }
515
516    /**
517     * Determines whether a type is a CHARSEQUENCE type used internally strings
518     *
519     * @return true if CharSequence (internal string) type, false otherwise
520     */
521    public boolean isCharSequence() {
522        return this.equals(Type.CHARSEQUENCE);
523    }
524
525    /**
526     * Determine if two types are equivalent, i.e. need no conversion
527     *
528     * @param type the second type to check
529     *
530     * @return true if types are equivalent, false otherwise
531     */
532    public boolean isEquivalentTo(final Type type) {
533        return this.weight() == type.weight() || isObject() && type.isObject();
534    }
535
536    /**
537     * Determine if a type can be assigned to from another
538     *
539     * @param type0 the first type to check
540     * @param type1 the second type to check
541     *
542     * @return true if type1 can be written to type2, false otherwise
543     */
544    public static boolean isAssignableFrom(final Type type0, final Type type1) {
545        if (type0.isObject() && type1.isObject()) {
546            return type0.weight() >= type1.weight();
547        }
548
549        return type0.weight() == type1.weight();
550    }
551
552    /**
553     * Determine if this type is assignable from another type
554     * @param type the type to check against
555     *
556     * @return true if "type" can be written to this type, false otherwise
557     */
558    public boolean isAssignableFrom(final Type type) {
559        return Type.isAssignableFrom(this, type);
560    }
561
562    /**
563     * Determines is this type is equivalent to another, i.e. needs no conversion
564     * to be assigned to it.
565     *
566     * @param type0 the first type to check
567     * @param type1 the second type to check
568     *
569     * @return true if this type is equivalent to type, false otherwise
570     */
571    public static boolean areEquivalent(final Type type0, final Type type1) {
572        return type0.isEquivalentTo(type1);
573    }
574
575    /**
576     * Determine the number of bytecode slots a type takes up
577     *
578     * @return the number of slots for this type, 1 or 2.
579     */
580    public int getSlots() {
581        return slots;
582    }
583
584    /**
585     * Returns the widest or most common of two types
586     *
587     * @param type0 type one
588     * @param type1 type two
589     *
590     * @return the widest type
591     */
592    public static Type widest(final Type type0, final Type type1) {
593        if (type0.isArray() && type1.isArray()) {
594            return ((ArrayType)type0).getElementType() == ((ArrayType)type1).getElementType() ? type0 : Type.OBJECT;
595        } else if (type0.isArray() != type1.isArray()) {
596            //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense
597            return Type.OBJECT;
598        } else if (type0.isObject() && type1.isObject() && type0.getTypeClass() != type1.getTypeClass()) {
599            // Object<type=String> and Object<type=ScriptFunction> will produce Object
600            // TODO: maybe find most specific common superclass?
601            return Type.OBJECT;
602        }
603        return type0.weight() > type1.weight() ? type0 : type1;
604    }
605
606    /**
607     * Returns the widest or most common of two types, given as classes
608     *
609     * @param type0 type one
610     * @param type1 type two
611     *
612     * @return the widest type
613     */
614    public static Class<?> widest(final Class<?> type0, final Class<?> type1) {
615        return widest(Type.typeFor(type0), Type.typeFor(type1)).getTypeClass();
616    }
617
618    /**
619     * When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to
620     * anything other than object. Note that this wouldn't be necessary if {@code Type.widest} did not allow
621     * boolean-to-number widening. Eventually, we should address it there, but it affects too many other parts of the
622     * system and is sometimes legitimate (e.g. whenever a boolean value would undergo ToNumber conversion anyway).
623     * @param t1 type 1
624     * @param t2 type 2
625     * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, in which case
626     * {@code Type.OBJECT} is returned.
627     */
628    public static Type widestReturnType(final Type t1, final Type t2) {
629        if (t1.isUnknown()) {
630            return t2;
631        } else if (t2.isUnknown()) {
632            return t1;
633        } else if(t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
634            return Type.OBJECT;
635        }
636        return Type.widest(t1, t2);
637    }
638
639    /**
640     * Returns a generic version of the type. Basically, if the type {@link #isObject()}, returns {@link #OBJECT},
641     * otherwise returns the type unchanged.
642     * @param type the type to generify
643     * @return the generified type
644     */
645    public static Type generic(final Type type) {
646        return type.isObject() ? Type.OBJECT : type;
647    }
648
649    /**
650     * Returns the narrowest or least common of two types
651     *
652     * @param type0 type one
653     * @param type1 type two
654     *
655     * @return the widest type
656     */
657    public static Type narrowest(final Type type0, final Type type1) {
658        return type0.narrowerThan(type1) ? type0 : type1;
659    }
660
661    /**
662     * Check whether this type is strictly narrower than another one
663     * @param type type to check against
664     * @return true if this type is strictly narrower
665     */
666    public boolean narrowerThan(final Type type) {
667        return weight() < type.weight();
668    }
669
670    /**
671     * Check whether this type is strictly wider than another one
672     * @param type type to check against
673     * @return true if this type is strictly wider
674     */
675    public boolean widerThan(final Type type) {
676        return weight() > type.weight();
677    }
678
679    /**
680     * Returns the widest or most common of two types, but no wider than "limit"
681     *
682     * @param type0 type one
683     * @param type1 type two
684     * @param limit limiting type
685     *
686     * @return the widest type, but no wider than limit
687     */
688    public static Type widest(final Type type0, final Type type1, final Type limit) {
689        final Type type = Type.widest(type0,  type1);
690        if (type.weight() > limit.weight()) {
691            return limit;
692        }
693        return type;
694    }
695
696    /**
697     * Returns the widest or most common of two types, but no narrower than "limit"
698     *
699     * @param type0 type one
700     * @param type1 type two
701     * @param limit limiting type
702     *
703     * @return the widest type, but no wider than limit
704     */
705    public static Type narrowest(final Type type0, final Type type1, final Type limit) {
706        final Type type = type0.weight() < type1.weight() ? type0 : type1;
707        if (type.weight() < limit.weight()) {
708            return limit;
709        }
710        return type;
711    }
712
713    /**
714     * Returns the narrowest of this type and another
715     *
716     * @param  other type to compare against
717     *
718     * @return the widest type
719     */
720    public Type narrowest(final Type other) {
721        return Type.narrowest(this, other);
722    }
723
724    /**
725     * Returns the widest of this type and another
726     *
727     * @param  other type to compare against
728     *
729     * @return the widest type
730     */
731    public Type widest(final Type other) {
732        return Type.widest(this, other);
733    }
734
735    /**
736     * Returns the weight of a type, used for type comparison
737     * between wider and narrower types
738     *
739     * @return the weight
740     */
741    int weight() {
742        return weight;
743    }
744
745    /**
746     * Return the descriptor of a type, used for e.g. signature
747     * generation
748     *
749     * @return the descriptor
750     */
751    public String getDescriptor() {
752        return descriptor;
753    }
754
755    /**
756     * Return the descriptor of a type, short version
757     * Used mainly for debugging purposes
758     *
759     * @return the short descriptor
760     */
761    public String getShortDescriptor() {
762        return descriptor;
763    }
764
765    @Override
766    public String toString() {
767        return name;
768    }
769
770    /**
771     * Return the (possibly cached) Type object for this class
772     *
773     * @param clazz the class to check
774     *
775     * @return the Type representing this class
776     */
777    public static Type typeFor(final Class<?> clazz) {
778        return cache.computeIfAbsent(clazz, (keyClass) -> {
779            assert !keyClass.isPrimitive() || keyClass == void.class;
780            return keyClass.isArray() ? new ArrayType(keyClass) : new ObjectType(keyClass);
781        });
782    }
783
784    @Override
785    public int compareTo(final Type o) {
786        return o.weight() - weight();
787    }
788
789    /**
790     * Common logic for implementing dup for all types
791     *
792     * @param method method visitor
793     * @param depth dup depth
794     *
795     * @return the type at the top of the stack afterwards
796     */
797    @Override
798    public Type dup(final MethodVisitor method, final int depth) {
799        return Type.dup(method, this, depth);
800    }
801
802    /**
803     * Common logic for implementing swap for all types
804     *
805     * @param method method visitor
806     * @param other  the type to swap with
807     *
808     * @return the type at the top of the stack afterwards, i.e. other
809     */
810    @Override
811    public Type swap(final MethodVisitor method, final Type other) {
812        Type.swap(method, this, other);
813        return other;
814    }
815
816    /**
817     * Common logic for implementing pop for all types
818     *
819     * @param method method visitor
820     *
821     * @return the type that was popped
822     */
823    @Override
824    public Type pop(final MethodVisitor method) {
825        Type.pop(method, this);
826        return this;
827    }
828
829    @Override
830    public Type loadEmpty(final MethodVisitor method) {
831        assert false : "unsupported operation";
832        return null;
833    }
834
835    /**
836     * Superclass logic for pop for all types
837     *
838     * @param method method emitter
839     * @param type   type to pop
840     */
841    protected static void pop(final MethodVisitor method, final Type type) {
842        method.visitInsn(type.isCategory2() ? POP2 : POP);
843    }
844
845    private static Type dup(final MethodVisitor method, final Type type, final int depth) {
846        final boolean       cat2 = type.isCategory2();
847
848        switch (depth) {
849        case 0:
850            method.visitInsn(cat2 ? DUP2 : DUP);
851            break;
852        case 1:
853            method.visitInsn(cat2 ? DUP2_X1 : DUP_X1);
854            break;
855        case 2:
856            method.visitInsn(cat2 ? DUP2_X2 : DUP_X2);
857            break;
858        default:
859            return null; //invalid depth
860        }
861
862        return type;
863    }
864
865    private static void swap(final MethodVisitor method, final Type above, final Type below) {
866        if (below.isCategory2()) {
867            if (above.isCategory2()) {
868                method.visitInsn(DUP2_X2);
869                method.visitInsn(POP2);
870            } else {
871                method.visitInsn(DUP_X2);
872                method.visitInsn(POP);
873            }
874        } else {
875            if (above.isCategory2()) {
876                method.visitInsn(DUP2_X1);
877                method.visitInsn(POP2);
878            } else {
879                method.visitInsn(SWAP);
880            }
881        }
882    }
883
884    /** Mappings between java classes and their Type singletons */
885    private static final ConcurrentMap<Class<?>, Type> cache = new ConcurrentHashMap<>();
886    private static final ConcurrentMap<String, Type> cacheByName = new ConcurrentHashMap<>();
887
888    /**
889     * This is the boolean singleton, used for all boolean types
890     */
891    public static final Type BOOLEAN = putInCache(new BooleanType());
892
893    /**
894     * This is an integer type, i.e INT, INT32.
895     */
896    public static final BitwiseType INT = putInCache(new IntType());
897
898    /**
899     * This is the number singleton, used for all number types
900     */
901    public static final NumericType NUMBER = putInCache(new NumberType());
902
903    /**
904     * This is the long singleton, used for all long types
905     */
906    public static final Type LONG = putInCache(new LongType());
907
908    /**
909     * A string singleton
910     */
911    public static final Type STRING = putInCache(new ObjectType(String.class));
912
913    /**
914     * This is the CharSequence singleton used to represent JS strings internally
915     * (either a {@code java.lang.String} or {@code jdk.nashorn.internal.runtime.ConsString}.
916     */
917    public static final Type CHARSEQUENCE = putInCache(new ObjectType(CharSequence.class));
918
919
920    /**
921     * This is the object singleton, used for all object types
922     */
923    public static final Type OBJECT = putInCache(new ObjectType());
924
925    /**
926     * A undefined singleton
927     */
928    public static final Type UNDEFINED = putInCache(new ObjectType(Undefined.class));
929
930    /**
931     * This is the singleton for ScriptObjects
932     */
933    public static final Type SCRIPT_OBJECT = putInCache(new ObjectType(ScriptObject.class));
934
935    /**
936     * This is the singleton for integer arrays
937     */
938    public static final ArrayType INT_ARRAY = putInCache(new ArrayType(int[].class) {
939        private static final long serialVersionUID = 1L;
940
941        @Override
942        public void astore(final MethodVisitor method) {
943            method.visitInsn(IASTORE);
944        }
945
946        @Override
947        public Type aload(final MethodVisitor method) {
948            method.visitInsn(IALOAD);
949            return INT;
950        }
951
952        @Override
953        public Type newarray(final MethodVisitor method) {
954            method.visitIntInsn(NEWARRAY, T_INT);
955            return this;
956        }
957
958        @Override
959        public Type getElementType() {
960            return INT;
961        }
962    });
963
964    /**
965     * This is the singleton for long arrays
966     */
967    public static final ArrayType LONG_ARRAY = putInCache(new ArrayType(long[].class) {
968        private static final long serialVersionUID = 1L;
969
970        @Override
971        public void astore(final MethodVisitor method) {
972            method.visitInsn(LASTORE);
973        }
974
975        @Override
976        public Type aload(final MethodVisitor method) {
977            method.visitInsn(LALOAD);
978            return LONG;
979        }
980
981        @Override
982        public Type newarray(final MethodVisitor method) {
983            method.visitIntInsn(NEWARRAY, T_LONG);
984            return this;
985        }
986
987        @Override
988        public Type getElementType() {
989            return LONG;
990        }
991    });
992
993    /**
994     * This is the singleton for numeric arrays
995     */
996    public static final ArrayType NUMBER_ARRAY = putInCache(new ArrayType(double[].class) {
997        private static final long serialVersionUID = 1L;
998
999        @Override
1000        public void astore(final MethodVisitor method) {
1001            method.visitInsn(DASTORE);
1002        }
1003
1004        @Override
1005        public Type aload(final MethodVisitor method) {
1006            method.visitInsn(DALOAD);
1007            return NUMBER;
1008        }
1009
1010        @Override
1011        public Type newarray(final MethodVisitor method) {
1012            method.visitIntInsn(NEWARRAY, T_DOUBLE);
1013            return this;
1014        }
1015
1016        @Override
1017        public Type getElementType() {
1018            return NUMBER;
1019        }
1020    });
1021
1022    /** This is the singleton for object arrays */
1023    public static final ArrayType OBJECT_ARRAY = putInCache(new ArrayType(Object[].class));
1024
1025    /** This type, always an object type, just a toString override */
1026    public static final Type THIS = new ObjectType() {
1027        private static final long serialVersionUID = 1L;
1028
1029        @Override
1030        public String toString() {
1031            return "this";
1032        }
1033    };
1034
1035    /** Scope type, always an object type, just a toString override */
1036    public static final Type SCOPE = new ObjectType() {
1037        private static final long serialVersionUID = 1L;
1038
1039        @Override
1040        public String toString() {
1041            return "scope";
1042        }
1043    };
1044
1045    private static interface Unknown {
1046        // EMPTY - used as a class that is absolutely not compatible with a type to represent "unknown"
1047    }
1048
1049    private abstract static class ValueLessType extends Type {
1050        private static final long serialVersionUID = 1L;
1051
1052        ValueLessType(final String name) {
1053            super(name, Unknown.class, MIN_WEIGHT, 1);
1054        }
1055
1056        @Override
1057        public Type load(final MethodVisitor method, final int slot) {
1058            throw new UnsupportedOperationException("load " + slot);
1059        }
1060
1061        @Override
1062        public void store(final MethodVisitor method, final int slot) {
1063            throw new UnsupportedOperationException("store " + slot);
1064        }
1065
1066        @Override
1067        public Type ldc(final MethodVisitor method, final Object c) {
1068            throw new UnsupportedOperationException("ldc " + c);
1069        }
1070
1071        @Override
1072        public Type loadUndefined(final MethodVisitor method) {
1073            throw new UnsupportedOperationException("load undefined");
1074        }
1075
1076        @Override
1077        public Type loadForcedInitializer(final MethodVisitor method) {
1078            throw new UnsupportedOperationException("load forced initializer");
1079        }
1080
1081        @Override
1082        public Type convert(final MethodVisitor method, final Type to) {
1083            throw new UnsupportedOperationException("convert => " + to);
1084        }
1085
1086        @Override
1087        public void _return(final MethodVisitor method) {
1088            throw new UnsupportedOperationException("return");
1089       }
1090
1091        @Override
1092        public Type add(final MethodVisitor method, final int programPoint) {
1093            throw new UnsupportedOperationException("add");
1094        }
1095    }
1096
1097    /**
1098     * This is the unknown type which is used as initial type for type
1099     * inference. It has the minimum type width
1100     */
1101    public static final Type UNKNOWN = new ValueLessType("<unknown>") {
1102        private static final long serialVersionUID = 1L;
1103
1104        @Override
1105        public String getDescriptor() {
1106            return "<unknown>";
1107        }
1108
1109        @Override
1110        public char getBytecodeStackType() {
1111            return 'U';
1112        }
1113    };
1114
1115    /**
1116     * This is the unknown type which is used as initial type for type
1117     * inference. It has the minimum type width
1118     */
1119    public static final Type SLOT_2 = new ValueLessType("<slot_2>") {
1120        private static final long serialVersionUID = 1L;
1121
1122        @Override
1123        public String getDescriptor() {
1124            return "<slot_2>";
1125        }
1126
1127        @Override
1128        public char getBytecodeStackType() {
1129            throw new UnsupportedOperationException("getBytecodeStackType");
1130        }
1131    };
1132
1133    private static <T extends Type> T putInCache(final T type) {
1134        cache.put(type.getTypeClass(), type);
1135        return type;
1136    }
1137
1138    /**
1139     * Read resolve
1140     * @return resolved type
1141     */
1142    protected final Object readResolve() {
1143        return Type.typeFor(clazz);
1144    }
1145}
1146