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