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