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