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