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