src/jdk/nashorn/internal/codegen/types/Type.java

Wed, 03 Sep 2014 14:33:34 +0200

author
attila
date
Wed, 03 Sep 2014 14:33:34 +0200
changeset 990
46647c4943ff
parent 963
e2497b11a021
child 1028
d79265f2fa92
permissions
-rw-r--r--

8056913: Limit the size of type info cache on disk
Reviewed-by: jlaskey, lagergren

     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  */
    26 package jdk.nashorn.internal.codegen.types;
    28 import static jdk.internal.org.objectweb.asm.Opcodes.DALOAD;
    29 import static jdk.internal.org.objectweb.asm.Opcodes.DASTORE;
    30 import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
    31 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2;
    32 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X1;
    33 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X2;
    34 import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X1;
    35 import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X2;
    36 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
    37 import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
    38 import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
    39 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
    40 import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
    41 import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
    42 import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
    43 import static jdk.internal.org.objectweb.asm.Opcodes.POP;
    44 import static jdk.internal.org.objectweb.asm.Opcodes.POP2;
    45 import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
    46 import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
    47 import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
    48 import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
    49 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
    51 import java.io.DataInput;
    52 import java.io.DataOutput;
    53 import java.io.IOException;
    54 import java.lang.invoke.CallSite;
    55 import java.lang.invoke.MethodHandle;
    56 import java.lang.invoke.MethodHandles;
    57 import java.lang.invoke.MethodType;
    58 import java.util.Map;
    59 import java.util.TreeMap;
    60 import java.util.concurrent.ConcurrentHashMap;
    61 import java.util.concurrent.ConcurrentMap;
    62 import jdk.internal.org.objectweb.asm.Handle;
    63 import jdk.internal.org.objectweb.asm.MethodVisitor;
    64 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
    65 import jdk.nashorn.internal.runtime.ScriptObject;
    66 import jdk.nashorn.internal.runtime.Undefined;
    67 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    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  */
    90 public abstract class Type implements Comparable<Type>, BytecodeOps {
    92     /** Human readable name for type */
    93     private final String name;
    95     /** Descriptor for type */
    96     private final String descriptor;
    98     /** The "weight" of the type. Used for picking widest/least specific common type */
    99     private final int weight;
   101     /** How many bytecode slots does this type occupy */
   102     private final int slots;
   104     /** The class for this type */
   105     private final Class<?> clazz;
   107     /** Weights are used to decide which types are "wider" than other types */
   108     protected static final int MIN_WEIGHT = -1;
   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;
   113     static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "mathBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class);
   115     static final Handle MATHBOOTSTRAP = new Handle(H_INVOKESTATIC, BOOTSTRAP.className(), "mathBootstrap", BOOTSTRAP.descriptor());
   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     }
   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     }
   141     /**
   142      * Get the Class representing this type
   143      * @return the class for this type
   144      */
   145     public Class<?> getTypeClass() {
   146         return clazz;
   147     }
   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     }
   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     }
   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();
   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     }
   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     }
   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     }
   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             }
   265         default:
   266             assert false : "Unknown itype : " + itype + " sort " + itype.getSort();
   267             break;
   268         }
   269         return null;
   270     }
   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     }
   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     }
   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     }
   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     }
   355     static jdk.internal.org.objectweb.asm.Type getInternalType(final String className) {
   356         return jdk.internal.org.objectweb.asm.Type.getType(className);
   357     }
   359     private jdk.internal.org.objectweb.asm.Type getInternalType() {
   360         return jdk.internal.org.objectweb.asm.Type.getType(getTypeClass());
   361     }
   363     private static jdk.internal.org.objectweb.asm.Type getInternalType(final Class<?> type) {
   364         return jdk.internal.org.objectweb.asm.Type.getType(type);
   365     }
   367     static void invokestatic(final MethodVisitor method, final Call call) {
   368         method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor(), false);
   369     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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         }
   529         return type0.weight() == type1.weight();
   530     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   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     }
   732     @Override
   733     public String toString() {
   734         return name;
   735     }
   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     }
   760     @Override
   761     public int compareTo(final Type o) {
   762         return o.weight() - weight();
   763     }
   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     }
   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     }
   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     }
   805     @Override
   806     public Type loadEmpty(final MethodVisitor method) {
   807         assert false : "unsupported operation";
   808         return null;
   809     }
   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     }
   821     private static Type dup(final MethodVisitor method, final Type type, final int depth) {
   822         final boolean       cat2 = type.isCategory2();
   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         }
   838         return type;
   839     }
   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     }
   860     /** Mappings between java classes and their Type singletons */
   861     private static final ConcurrentMap<Class<?>, Type> cache = new ConcurrentHashMap<>();
   863     /**
   864      * This is the boolean singleton, used for all boolean types
   865      */
   866     public static final Type BOOLEAN = putInCache(new BooleanType());
   868     /**
   869      * This is an integer type, i.e INT, INT32.
   870      */
   871     public static final BitwiseType INT = putInCache(new IntType());
   873     /**
   874      * This is the number singleton, used for all number types
   875      */
   876     public static final NumericType NUMBER = putInCache(new NumberType());
   878     /**
   879      * This is the long singleton, used for all long types
   880      */
   881     public static final BitwiseType LONG = putInCache(new LongType());
   883     /**
   884      * A string singleton
   885      */
   886     public static final Type STRING = putInCache(new ObjectType(String.class));
   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));
   895     /**
   896      * This is the object singleton, used for all object types
   897      */
   898     public static final Type OBJECT = putInCache(new ObjectType());
   900     /**
   901      * A undefined singleton
   902      */
   903     public static final Type UNDEFINED = putInCache(new ObjectType(Undefined.class));
   905     /**
   906      * This is the singleton for ScriptObjects
   907      */
   908     public static final Type SCRIPT_OBJECT = putInCache(new ObjectType(ScriptObject.class));
   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         }
   919         @Override
   920         public Type aload(final MethodVisitor method) {
   921             method.visitInsn(IALOAD);
   922             return INT;
   923         }
   925         @Override
   926         public Type newarray(final MethodVisitor method) {
   927             method.visitIntInsn(NEWARRAY, T_INT);
   928             return this;
   929         }
   931         @Override
   932         public Type getElementType() {
   933             return INT;
   934         }
   935     };
   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         }
   946         @Override
   947         public Type aload(final MethodVisitor method) {
   948             method.visitInsn(LALOAD);
   949             return LONG;
   950         }
   952         @Override
   953         public Type newarray(final MethodVisitor method) {
   954             method.visitIntInsn(NEWARRAY, T_LONG);
   955             return this;
   956         }
   958         @Override
   959         public Type getElementType() {
   960             return LONG;
   961         }
   962     };
   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         }
   973         @Override
   974         public Type aload(final MethodVisitor method) {
   975             method.visitInsn(DALOAD);
   976             return NUMBER;
   977         }
   979         @Override
   980         public Type newarray(final MethodVisitor method) {
   981             method.visitIntInsn(NEWARRAY, T_DOUBLE);
   982             return this;
   983         }
   985         @Override
   986         public Type getElementType() {
   987             return NUMBER;
   988         }
   989     };
   991     /** Singleton for method handle arrays used for properties etc. */
   992     public static final ArrayType METHODHANDLE_ARRAY = putInCache(new ArrayType(MethodHandle[].class));
   994     /** This is the singleton for string arrays */
   995     public static final ArrayType STRING_ARRAY = putInCache(new ArrayType(String[].class));
   997     /** This is the singleton for object arrays */
   998     public static final ArrayType OBJECT_ARRAY = putInCache(new ArrayType(Object[].class));
  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";
  1006     };
  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";
  1014     };
  1016     private static interface Unknown {
  1017         // EMPTY - used as a class that is absolutely not compatible with a type to represent "unknown"
  1020     private abstract static class ValueLessType extends Type {
  1022         ValueLessType(final String name) {
  1023             super(name, Unknown.class, MIN_WEIGHT, 1);
  1026         @Override
  1027         public Type load(final MethodVisitor method, final int slot) {
  1028             throw new UnsupportedOperationException("load " + slot);
  1031         @Override
  1032         public void store(final MethodVisitor method, final int slot) {
  1033             throw new UnsupportedOperationException("store " + slot);
  1036         @Override
  1037         public Type ldc(final MethodVisitor method, final Object c) {
  1038             throw new UnsupportedOperationException("ldc " + c);
  1041         @Override
  1042         public Type loadUndefined(final MethodVisitor method) {
  1043             throw new UnsupportedOperationException("load undefined");
  1046         @Override
  1047         public Type loadForcedInitializer(final MethodVisitor method) {
  1048             throw new UnsupportedOperationException("load forced initializer");
  1051         @Override
  1052         public Type convert(final MethodVisitor method, final Type to) {
  1053             throw new UnsupportedOperationException("convert => " + to);
  1056         @Override
  1057         public void _return(final MethodVisitor method) {
  1058             throw new UnsupportedOperationException("return");
  1061         @Override
  1062         public Type add(final MethodVisitor method, final int programPoint) {
  1063             throw new UnsupportedOperationException("add");
  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>";
  1077         @Override
  1078         public char getBytecodeStackType() {
  1079             return 'U';
  1081     };
  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>") {
  1089         @Override
  1090         public String getDescriptor() {
  1091             return "<slot_2>";
  1094         @Override
  1095         public char getBytecodeStackType() {
  1096             throw new UnsupportedOperationException("getBytecodeStackType");
  1098     };
  1100     private static <T extends Type> T putInCache(final T type) {
  1101         cache.put(type.getTypeClass(), type);
  1102         return type;

mercurial