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