Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

MultiType.java 9.3KB

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