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.

MultiType.java 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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.HashMap;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import javassist.CtClass;
  21. /**
  22. * MultiType represents an unresolved type. Whenever two {@code Type}
  23. * instances are merged, if they share more than one super type (either an
  24. * interface or a superclass), then a {@code MultiType} is used to
  25. * represent the possible super types. The goal of a {@code MultiType}
  26. * is to reduce the set of possible types down to a single resolved type. This
  27. * is done by eliminating non-assignable types from the typeset when the
  28. * {@code MultiType} is passed as an argument to
  29. * {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting
  30. * types during a merge.
  31. *
  32. * Note: Currently the {@code MultiType} instance is reused as much
  33. * as possible so that updates are visible from all frames. In addition, all
  34. * {@code MultiType} merge paths are also updated. This is somewhat
  35. * hackish, but it appears to handle most scenarios.
  36. *
  37. * @author Jason T. Greene
  38. */
  39. /* TODO - A better, but more involved, approach would be to track the instruction
  40. * offset that resulted in the creation of this type, and
  41. * whenever the typeset changes, to force a merge on that position. This
  42. * would require creating a new MultiType instance every time the typeset
  43. * changes, and somehow communicating assignment changes to the Analyzer
  44. */
  45. public class MultiType extends Type {
  46. private Map interfaces;
  47. private Type resolved;
  48. private Type potentialClass;
  49. private MultiType mergeSource;
  50. private boolean changed = false;
  51. public MultiType(Map interfaces) {
  52. this(interfaces, null);
  53. }
  54. public MultiType(Map interfaces, Type potentialClass) {
  55. super(null);
  56. this.interfaces = interfaces;
  57. this.potentialClass = potentialClass;
  58. }
  59. /**
  60. * Gets the class that corresponds with this type. If this information
  61. * is not yet known, java.lang.Object will be returned.
  62. */
  63. public CtClass getCtClass() {
  64. if (resolved != null)
  65. return resolved.getCtClass();
  66. return Type.OBJECT.getCtClass();
  67. }
  68. /**
  69. * Always returns null since this type is never used for an array.
  70. */
  71. public Type getComponent() {
  72. return null;
  73. }
  74. /**
  75. * Always returns 1, since this type is a reference.
  76. */
  77. public int getSize() {
  78. return 1;
  79. }
  80. /**
  81. * Always reutnrs false since this type is never used for an array
  82. */
  83. public boolean isArray() {
  84. return false;
  85. }
  86. /**
  87. * Returns true if the internal state has changed.
  88. */
  89. boolean popChanged() {
  90. boolean changed = this.changed;
  91. this.changed = false;
  92. return changed;
  93. }
  94. public boolean isAssignableFrom(Type type) {
  95. throw new UnsupportedOperationException("Not implemented");
  96. }
  97. public boolean isAssignableTo(Type type) {
  98. if (resolved != null)
  99. return type.isAssignableFrom(resolved);
  100. if (Type.OBJECT.equals(type))
  101. return true;
  102. if (potentialClass != null && !type.isAssignableFrom(potentialClass))
  103. potentialClass = null;
  104. Map map = mergeMultiAndSingle(this, type);
  105. if (map.size() == 1 && potentialClass == null) {
  106. // Update previous merge paths to the same resolved type
  107. resolved = Type.get((CtClass)map.values().iterator().next());
  108. propogateResolved();
  109. return true;
  110. }
  111. // Keep all previous merge paths up to date
  112. if (map.size() >= 1) {
  113. interfaces = map;
  114. propogateState();
  115. return true;
  116. }
  117. if (potentialClass != null) {
  118. resolved = potentialClass;
  119. propogateResolved();
  120. return true;
  121. }
  122. return false;
  123. }
  124. private void propogateState() {
  125. MultiType source = mergeSource;
  126. while (source != null) {
  127. source.interfaces = interfaces;
  128. source.potentialClass = potentialClass;
  129. source = source.mergeSource;
  130. }
  131. }
  132. private void propogateResolved() {
  133. MultiType source = mergeSource;
  134. while (source != null) {
  135. source.resolved = resolved;
  136. source = source.mergeSource;
  137. }
  138. }
  139. /**
  140. * Always returns true, since this type is always a reference.
  141. *
  142. * @return true
  143. */
  144. public boolean isReference() {
  145. return true;
  146. }
  147. private Map getAllMultiInterfaces(MultiType type) {
  148. Map map = new HashMap();
  149. Iterator iter = type.interfaces.values().iterator();
  150. while (iter.hasNext()) {
  151. CtClass intf = (CtClass)iter.next();
  152. map.put(intf.getName(), intf);
  153. getAllInterfaces(intf, map);
  154. }
  155. return map;
  156. }
  157. private Map mergeMultiInterfaces(MultiType type1, MultiType type2) {
  158. Map map1 = getAllMultiInterfaces(type1);
  159. Map map2 = getAllMultiInterfaces(type2);
  160. return findCommonInterfaces(map1, map2);
  161. }
  162. private Map mergeMultiAndSingle(MultiType multi, Type single) {
  163. Map map1 = getAllMultiInterfaces(multi);
  164. Map map2 = getAllInterfaces(single.getCtClass(), null);
  165. return findCommonInterfaces(map1, map2);
  166. }
  167. private boolean inMergeSource(MultiType source) {
  168. while (source != null) {
  169. if (source == this)
  170. return true;
  171. source = source.mergeSource;
  172. }
  173. return false;
  174. }
  175. public Type merge(Type type) {
  176. if (this == type)
  177. return this;
  178. if (type == UNINIT)
  179. return this;
  180. if (type == BOGUS)
  181. return BOGUS;
  182. if (type == null)
  183. return this;
  184. if (resolved != null)
  185. return resolved.merge(type);
  186. if (potentialClass != null) {
  187. Type mergePotential = potentialClass.merge(type);
  188. if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) {
  189. potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential;
  190. changed = true;
  191. }
  192. }
  193. Map merged;
  194. if (type instanceof MultiType) {
  195. MultiType multi = (MultiType)type;
  196. if (multi.resolved != null) {
  197. merged = mergeMultiAndSingle(this, multi.resolved);
  198. } else {
  199. merged = mergeMultiInterfaces(multi, this);
  200. if (! inMergeSource(multi))
  201. mergeSource = multi;
  202. }
  203. } else {
  204. merged = mergeMultiAndSingle(this, type);
  205. }
  206. // Keep all previous merge paths up to date
  207. if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) {
  208. // Check for changes
  209. if (merged.size() != interfaces.size()) {
  210. changed = true;
  211. } else if (changed == false){
  212. Iterator iter = merged.keySet().iterator();
  213. while (iter.hasNext())
  214. if (! interfaces.containsKey(iter.next()))
  215. changed = true;
  216. }
  217. interfaces = merged;
  218. propogateState();
  219. return this;
  220. }
  221. if (merged.size() == 1) {
  222. resolved = Type.get((CtClass) merged.values().iterator().next());
  223. } else if (potentialClass != null){
  224. resolved = potentialClass;
  225. } else {
  226. resolved = OBJECT;
  227. }
  228. propogateResolved();
  229. return resolved;
  230. }
  231. public boolean equals(Object o) {
  232. if (! (o instanceof MultiType))
  233. return false;
  234. MultiType multi = (MultiType) o;
  235. if (resolved != null)
  236. return resolved.equals(multi.resolved);
  237. else if (multi.resolved != null)
  238. return false;
  239. return interfaces.keySet().equals(multi.interfaces.keySet());
  240. }
  241. public String toString() {
  242. if (resolved != null)
  243. return resolved.toString();
  244. StringBuffer buffer = new StringBuffer("{");
  245. Iterator iter = interfaces.keySet().iterator();
  246. while (iter.hasNext()) {
  247. buffer.append(iter.next());
  248. buffer.append(", ");
  249. }
  250. buffer.setLength(buffer.length() - 2);
  251. if (potentialClass != null)
  252. buffer.append(", *").append(potentialClass.toString());
  253. buffer.append("}");
  254. return buffer.toString();
  255. }
  256. }