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