You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Type.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.bytecode.analysis;
  17. import java.util.ArrayList;
  18. import java.util.HashMap;
  19. import java.util.IdentityHashMap;
  20. import java.util.Iterator;
  21. import java.util.Map;
  22. import javassist.ClassPool;
  23. import javassist.CtClass;
  24. import javassist.NotFoundException;
  25. /**
  26. * Represents a JVM type in data-flow analysis. This abstraction is necessary since
  27. * a JVM type not only includes all normal Java types, but also a few special types
  28. * that are used by the JVM internally. See the static field types on this class for
  29. * more info on these special types.
  30. *
  31. * All primitive and special types reuse the same instance, so identity comparison can
  32. * be used when examining them. Normal java types must use {@link #equals(Object)} to
  33. * compare type instances.
  34. *
  35. * In most cases, applications which consume this API, only need to call {@link #getCtClass()}
  36. * to obtain the needed type information.
  37. *
  38. * @author Jason T. Greene
  39. */
  40. public class Type {
  41. private final CtClass clazz;
  42. private final boolean special;
  43. private static final Map prims = new IdentityHashMap();
  44. /** Represents the double primitive type */
  45. public static final Type DOUBLE = new Type(CtClass.doubleType);
  46. /** Represents the boolean primitive type */
  47. public static final Type BOOLEAN = new Type(CtClass.booleanType);
  48. /** Represents the long primitive type */
  49. public static final Type LONG = new Type(CtClass.longType);
  50. /** Represents the char primitive type */
  51. public static final Type CHAR = new Type(CtClass.charType);
  52. /** Represents the byte primitive type */
  53. public static final Type BYTE = new Type(CtClass.byteType);
  54. /** Represents the short primitive type */
  55. public static final Type SHORT = new Type(CtClass.shortType);
  56. /** Represents the integer primitive type */
  57. public static final Type INTEGER = new Type(CtClass.intType);
  58. /** Represents the float primitive type */
  59. public static final Type FLOAT = new Type(CtClass.floatType);
  60. /** Represents the void primitive type */
  61. public static final Type VOID = new Type(CtClass.voidType);
  62. /**
  63. * Represents an unknown, or null type. This occurs when aconst_null is used.
  64. * It is important not to treat this type as java.lang.Object, since a null can
  65. * be assigned to any reference type. The analyzer will replace these with
  66. * an actual known type if it can be determined by a merged path with known type
  67. * information. If this type is encountered on a frame then it is guaranteed to
  68. * be null, and the type information is simply not available. Any attempts to
  69. * infer the type, without further information from the compiler would be a guess.
  70. */
  71. public static final Type UNINIT = new Type(null);
  72. /**
  73. * Represents an internal JVM return address, which is used by the RET
  74. * instruction to return to a JSR that invoked the subroutine.
  75. */
  76. public static final Type RETURN_ADDRESS = new Type(null, true);
  77. /** A placeholder used by the analyzer for the second word position of a double-word type */
  78. public static final Type TOP = new Type(null, true);
  79. /**
  80. * Represents a non-accessible value. Code cannot access the value this type
  81. * represents. It occurs when bytecode reuses a local variable table
  82. * position with non-mergable types. An example would be compiled code which
  83. * uses the same position for a primitive type in one branch, and a reference type
  84. * in another branch.
  85. */
  86. public static final Type BOGUS = new Type(null, true);
  87. /** Represents the java.lang.Object reference type */
  88. public static final Type OBJECT = lookupType("java.lang.Object");
  89. /** Represents the java.io.Serializable reference type */
  90. public static final Type SERIALIZABLE = lookupType("java.io.Serializable");
  91. /** Represents the java.lang.Coneable reference type */
  92. public static final Type CLONEABLE = lookupType("java.lang.Cloneable");
  93. /** Represents the java.lang.Throwable reference type */
  94. public static final Type THROWABLE = lookupType("java.lang.Throwable");
  95. static {
  96. prims.put(CtClass.doubleType, DOUBLE);
  97. prims.put(CtClass.longType, LONG);
  98. prims.put(CtClass.charType, CHAR);
  99. prims.put(CtClass.shortType, SHORT);
  100. prims.put(CtClass.intType, INTEGER);
  101. prims.put(CtClass.floatType, FLOAT);
  102. prims.put(CtClass.byteType, BYTE);
  103. prims.put(CtClass.booleanType, BOOLEAN);
  104. prims.put(CtClass.voidType, VOID);
  105. }
  106. /**
  107. * Obtain the Type for a given class. If the class is a primitive,
  108. * the the unique type instance for the primitive will be returned.
  109. * Otherwise a new Type instance representing the class is returned.
  110. *
  111. * @param clazz The java class
  112. * @return a type instance for this class
  113. */
  114. public static Type get(CtClass clazz) {
  115. Type type = (Type)prims.get(clazz);
  116. return type != null ? type : new Type(clazz);
  117. }
  118. private static Type lookupType(String name) {
  119. try {
  120. return new Type(ClassPool.getDefault().get(name));
  121. } catch (NotFoundException e) {
  122. throw new RuntimeException(e);
  123. }
  124. }
  125. Type(CtClass clazz) {
  126. this(clazz, false);
  127. }
  128. private Type(CtClass clazz, boolean special) {
  129. this.clazz = clazz;
  130. this.special = special;
  131. }
  132. // Used to indicate a merge internally triggered a change
  133. boolean popChanged() {
  134. return false;
  135. }
  136. /**
  137. * Gets the word size of this type. Double-word types, such as long and double
  138. * will occupy two positions on the local variable table or stack.
  139. *
  140. * @return the number of words needed to hold this type
  141. */
  142. public int getSize() {
  143. return clazz == CtClass.doubleType || clazz == CtClass.longType || this == TOP ? 2 : 1;
  144. }
  145. /**
  146. * Returns the class this type represents. If the type is special, null will be returned.
  147. *
  148. * @return the class for this type, or null if special
  149. */
  150. public CtClass getCtClass() {
  151. return clazz;
  152. }
  153. /**
  154. * Returns whether or not this type is a normal java reference, i.e. it is or extends java.lang.Object.
  155. *
  156. * @return true if a java reference, false if a primitive or special
  157. */
  158. public boolean isReference() {
  159. return !special && (clazz == null || !clazz.isPrimitive());
  160. }
  161. /**
  162. * Returns whether or not the type is special. A special type is one that is either used
  163. * for internal tracking, or is only used internally by the JVM.
  164. *
  165. * @return true if special, false if not
  166. */
  167. public boolean isSpecial() {
  168. return special;
  169. }
  170. /**
  171. * Returns whether or not this type is an array.
  172. *
  173. * @return true if an array, false if not
  174. */
  175. public boolean isArray() {
  176. return clazz != null && clazz.isArray();
  177. }
  178. /**
  179. * Returns the number of dimensions of this array. If the type is not an
  180. * array zero is returned.
  181. *
  182. * @return zero if not an array, otherwise the number of array dimensions.
  183. */
  184. public int getDimensions() {
  185. if (!isArray()) return 0;
  186. String name = clazz.getName();
  187. int pos = name.length() - 1;
  188. int count = 0;
  189. while (name.charAt(pos) == ']' ) {
  190. pos -= 2;
  191. count++;
  192. }
  193. return count;
  194. }
  195. /**
  196. * Returns the array component if this type is an array. If the type
  197. * is not an array null is returned.
  198. *
  199. * @return the array component if an array, otherwise null
  200. */
  201. public Type getComponent() {
  202. if (this.clazz == null || !this.clazz.isArray())
  203. return null;
  204. CtClass component;
  205. try {
  206. component = this.clazz.getComponentType();
  207. } catch (NotFoundException e) {
  208. throw new RuntimeException(e);
  209. }
  210. Type type = (Type)prims.get(component);
  211. return (type != null) ? type : new Type(component);
  212. }
  213. /**
  214. * Determines whether this type is assignable, to the passed type.
  215. * A type is assignable to another if it is either the same type, or
  216. * a sub-type.
  217. *
  218. * @param type the type to test assignability to
  219. * @return true if this is assignable to type, otherwise false
  220. */
  221. public boolean isAssignableFrom(Type type) {
  222. if (this == type)
  223. return true;
  224. if ((type == UNINIT && isReference()) || this == UNINIT && type.isReference())
  225. return true;
  226. if (type instanceof MultiType)
  227. return ((MultiType)type).isAssignableTo(this);
  228. if (type instanceof MultiArrayType)
  229. return ((MultiArrayType)type).isAssignableTo(this);
  230. // Primitives and Special types must be identical
  231. if (clazz == null || clazz.isPrimitive())
  232. return false;
  233. try {
  234. return type.clazz.subtypeOf(clazz);
  235. } catch (Exception e) {
  236. throw new RuntimeException(e);
  237. }
  238. }
  239. /**
  240. * Finds the common base type, or interface which both this and the specified
  241. * type can be assigned. If there is more than one possible answer, then a {@link MultiType},
  242. * or a {@link MultiArrayType} is returned. Multi-types have special rules,
  243. * and successive merges and assignment tests on them will alter their internal state,
  244. * as well as other multi-types they have been merged with. This method is used by
  245. * the data-flow analyzer to merge the type state from multiple branches.
  246. *
  247. * @param type the type to merge with
  248. * @return the merged type
  249. */
  250. public Type merge(Type type) {
  251. if (type == this)
  252. return this;
  253. if (type == null)
  254. return this;
  255. if (type == Type.UNINIT)
  256. return this;
  257. if (this == Type.UNINIT)
  258. return type;
  259. // Unequal primitives and special types can not be merged
  260. if (! type.isReference() || ! this.isReference())
  261. return BOGUS;
  262. // Centralize merging of multi-interface types
  263. if (type instanceof MultiType)
  264. return type.merge(this);
  265. if (type.isArray() && this.isArray())
  266. return mergeArray(type);
  267. try {
  268. return mergeClasses(type);
  269. } catch (NotFoundException e) {
  270. throw new RuntimeException(e);
  271. }
  272. }
  273. Type getRootComponent(Type type) {
  274. while (type.isArray())
  275. type = type.getComponent();
  276. return type;
  277. }
  278. private Type createArray(Type rootComponent, int dims) {
  279. if (rootComponent instanceof MultiType)
  280. return new MultiArrayType((MultiType) rootComponent, dims);
  281. String name = arrayName(rootComponent.clazz.getName(), dims);
  282. Type type;
  283. try {
  284. type = Type.get(getClassPool(rootComponent).get(name));
  285. } catch (NotFoundException e) {
  286. throw new RuntimeException(e);
  287. }
  288. return type;
  289. }
  290. String arrayName(String component, int dims) {
  291. // Using char[] since we have no StringBuilder in JDK4, and StringBuffer is slow.
  292. // Although, this is more efficient even if we did have one.
  293. int i = component.length();
  294. int size = i + dims * 2;
  295. char[] string = new char[size];
  296. component.getChars(0, i, string, 0);
  297. while (i < size) {
  298. string[i++] = '[';
  299. string[i++] = ']';
  300. }
  301. component = new String(string);
  302. return component;
  303. }
  304. private ClassPool getClassPool(Type rootComponent) {
  305. ClassPool pool = rootComponent.clazz.getClassPool();
  306. return pool != null ? pool : ClassPool.getDefault();
  307. }
  308. private Type mergeArray(Type type) {
  309. Type typeRoot = getRootComponent(type);
  310. Type thisRoot = getRootComponent(this);
  311. int typeDims = type.getDimensions();
  312. int thisDims = this.getDimensions();
  313. // Array commponents can be merged when the dimensions are equal
  314. if (typeDims == thisDims) {
  315. Type mergedComponent = thisRoot.merge(typeRoot);
  316. // If the components can not be merged (a primitive component mixed with a different type)
  317. // then Object is the common type.
  318. if (mergedComponent == Type.BOGUS)
  319. return Type.OBJECT;
  320. return createArray(mergedComponent, thisDims);
  321. }
  322. Type targetRoot;
  323. int targetDims;
  324. if (typeDims < thisDims) {
  325. targetRoot = typeRoot;
  326. targetDims = typeDims;
  327. } else {
  328. targetRoot = thisRoot;
  329. targetDims = thisDims;
  330. }
  331. // Special case, arrays are cloneable and serializable, so prefer them when dimensions differ
  332. if (eq(CLONEABLE.clazz, targetRoot.clazz) || eq(SERIALIZABLE.clazz, targetRoot.clazz))
  333. return createArray(targetRoot, targetDims);
  334. return createArray(OBJECT, targetDims);
  335. }
  336. private static CtClass findCommonSuperClass(CtClass one, CtClass two) throws NotFoundException {
  337. CtClass deep = one;
  338. CtClass shallow = two;
  339. CtClass backupShallow = shallow;
  340. CtClass backupDeep = deep;
  341. // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly
  342. for (;;) {
  343. // In case we get lucky, and find a match early
  344. if (eq(deep, shallow) && deep.getSuperclass() != null)
  345. return deep;
  346. CtClass deepSuper = deep.getSuperclass();
  347. CtClass shallowSuper = shallow.getSuperclass();
  348. if (shallowSuper == null) {
  349. // right, now reset shallow
  350. shallow = backupShallow;
  351. break;
  352. }
  353. if (deepSuper == null) {
  354. // wrong, swap them, since deep is now useless, its our tmp before we swap it
  355. deep = backupDeep;
  356. backupDeep = backupShallow;
  357. backupShallow = deep;
  358. deep = shallow;
  359. shallow = backupShallow;
  360. break;
  361. }
  362. deep = deepSuper;
  363. shallow = shallowSuper;
  364. }
  365. // Phase 2 - Move deepBackup up by (deep end - deep)
  366. for (;;) {
  367. deep = deep.getSuperclass();
  368. if (deep == null)
  369. break;
  370. backupDeep = backupDeep.getSuperclass();
  371. }
  372. deep = backupDeep;
  373. // Phase 3 - The hierarchy positions are now aligned
  374. // The common super class is easy to find now
  375. while (!eq(deep, shallow)) {
  376. deep = deep.getSuperclass();
  377. shallow = shallow.getSuperclass();
  378. }
  379. return deep;
  380. }
  381. private Type mergeClasses(Type type) throws NotFoundException {
  382. CtClass superClass = findCommonSuperClass(this.clazz, type.clazz);
  383. // If its Object, then try and find a common interface(s)
  384. if (superClass.getSuperclass() == null) {
  385. Map interfaces = findCommonInterfaces(type);
  386. if (interfaces.size() == 1)
  387. return new Type((CtClass) interfaces.values().iterator().next());
  388. if (interfaces.size() > 1)
  389. return new MultiType(interfaces);
  390. // Only Object is in common
  391. return new Type(superClass);
  392. }
  393. // Check for a common interface that is not on the found supertype
  394. Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass);
  395. if (commonDeclared.size() > 0) {
  396. return new MultiType(commonDeclared, new Type(superClass));
  397. }
  398. return new Type(superClass);
  399. }
  400. private Map findCommonInterfaces(Type type) {
  401. Map typeMap = getAllInterfaces(type.clazz, null);
  402. Map thisMap = getAllInterfaces(this.clazz, null);
  403. return findCommonInterfaces(typeMap, thisMap);
  404. }
  405. private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) {
  406. Map typeMap = getDeclaredInterfaces(type.clazz, null);
  407. Map thisMap = getDeclaredInterfaces(this.clazz, null);
  408. Map excludeMap = getAllInterfaces(exclude, null);
  409. Iterator i = excludeMap.keySet().iterator();
  410. while (i.hasNext()) {
  411. Object intf = i.next();
  412. typeMap.remove(intf);
  413. thisMap.remove(intf);
  414. }
  415. return findCommonInterfaces(typeMap, thisMap);
  416. }
  417. Map findCommonInterfaces(Map typeMap, Map alterMap) {
  418. Iterator i = alterMap.keySet().iterator();
  419. while (i.hasNext()) {
  420. if (! typeMap.containsKey(i.next()))
  421. i.remove();
  422. }
  423. // Reduce to subinterfaces
  424. // This does not need to be recursive since we make a copy,
  425. // and that copy contains all super types for the whole hierarchy
  426. i = new ArrayList(alterMap.values()).iterator();
  427. while (i.hasNext()) {
  428. CtClass intf = (CtClass) i.next();
  429. CtClass[] interfaces;
  430. try {
  431. interfaces = intf.getInterfaces();
  432. } catch (NotFoundException e) {
  433. throw new RuntimeException(e);
  434. }
  435. for (int c = 0; c < interfaces.length; c++)
  436. alterMap.remove(interfaces[c].getName());
  437. }
  438. return alterMap;
  439. }
  440. Map getAllInterfaces(CtClass clazz, Map map) {
  441. if (map == null)
  442. map = new HashMap();
  443. if (clazz.isInterface())
  444. map.put(clazz.getName(), clazz);
  445. do {
  446. try {
  447. CtClass[] interfaces = clazz.getInterfaces();
  448. for (int i = 0; i < interfaces.length; i++) {
  449. CtClass intf = interfaces[i];
  450. map.put(intf.getName(), intf);
  451. getAllInterfaces(intf, map);
  452. }
  453. clazz = clazz.getSuperclass();
  454. } catch (NotFoundException e) {
  455. throw new RuntimeException(e);
  456. }
  457. } while (clazz != null);
  458. return map;
  459. }
  460. Map getDeclaredInterfaces(CtClass clazz, Map map) {
  461. if (map == null)
  462. map = new HashMap();
  463. if (clazz.isInterface())
  464. map.put(clazz.getName(), clazz);
  465. CtClass[] interfaces;
  466. try {
  467. interfaces = clazz.getInterfaces();
  468. } catch (NotFoundException e) {
  469. throw new RuntimeException(e);
  470. }
  471. for (int i = 0; i < interfaces.length; i++) {
  472. CtClass intf = interfaces[i];
  473. map.put(intf.getName(), intf);
  474. getDeclaredInterfaces(intf, map);
  475. }
  476. return map;
  477. }
  478. public boolean equals(Object o) {
  479. if (! (o instanceof Type))
  480. return false;
  481. return o.getClass() == getClass() && eq(clazz, ((Type)o).clazz);
  482. }
  483. static boolean eq(CtClass one, CtClass two) {
  484. return one == two || (one != null && two != null && one.getName().equals(two.getName()));
  485. }
  486. public String toString() {
  487. if (this == BOGUS)
  488. return "BOGUS";
  489. if (this == UNINIT)
  490. return "UNINIT";
  491. if (this == RETURN_ADDRESS)
  492. return "RETURN ADDRESS";
  493. if (this == TOP)
  494. return "TOP";
  495. return clazz == null ? "null" : clazz.getName();
  496. }
  497. }