/src/kilim/mirrors/Detector.java

http://github.com/kilim/kilim · Java · 228 lines · 170 code · 41 blank · 17 comment · 49 complexity · a70b1d5f55ec4fcb2e7c9396a5706f5e MD5 · raw file

  1. /* Copyright (c) 2006, Sriram Srinivasan
  2. *
  3. * You may distribute this software under the terms of the license
  4. * specified in the file "License"
  5. */
  6. package kilim.mirrors;
  7. import static kilim.Constants.D_OBJECT;
  8. import java.util.ArrayList;
  9. import kilim.NotPausable;
  10. import kilim.Pausable;
  11. import kilim.mirrors.CachedClassMirrors.ClassMirror;
  12. import kilim.mirrors.CachedClassMirrors.MethodMirror;
  13. /**
  14. * Utility class to check if a method has been marked pausable
  15. *
  16. */
  17. public class Detector {
  18. public static final int METHOD_NOT_FOUND_OR_PAUSABLE = 0; // either not found, or not pausable if found.
  19. public static final int PAUSABLE_METHOD_FOUND = 1; // known to be pausable
  20. public static final int METHOD_NOT_PAUSABLE = 2; // known to be not pausable
  21. // Note that we don't have the kilim package itself in the following list.
  22. static final String[] STANDARD_DONT_CHECK_LIST = { "java.", "javax." };
  23. public final CachedClassMirrors mirrors;
  24. public Detector(CachedClassMirrors mirrors) {
  25. this.mirrors = mirrors;
  26. NOT_PAUSABLE = mirrors.mirror(NotPausable.class);
  27. PAUSABLE = mirrors.mirror(Pausable.class);
  28. OBJECT = mirrors.mirror(Object.class);
  29. }
  30. ClassMirror NOT_PAUSABLE, PAUSABLE, OBJECT;
  31. public boolean isPausable(String className, String methodName, String desc) {
  32. return getPausableStatus(className, methodName, desc) == PAUSABLE_METHOD_FOUND;
  33. }
  34. /**
  35. * @return one of METHOD_NOT_FOUND, PAUSABLE_METHOD_FOUND, METHOD_NOT_PAUSABLE
  36. */
  37. static boolean isNonPausableClass(String className) {
  38. return className == null || className.charAt(0) == '[' ||
  39. className.startsWith("java.") || className.startsWith("javax.");
  40. }
  41. static boolean isNonPausableMethod(String methodName) {
  42. return methodName.endsWith("init>");
  43. }
  44. public int getPausableStatus(String className, String methodName, String desc) {
  45. int ret = METHOD_NOT_FOUND_OR_PAUSABLE;
  46. // array methods (essentially methods deferred to Object (clone, wait etc)
  47. // and constructor methods are not pausable
  48. if (isNonPausableClass(className) || isNonPausableMethod(methodName)) {
  49. return METHOD_NOT_FOUND_OR_PAUSABLE;
  50. }
  51. className = className.replace('/', '.');
  52. try {
  53. MethodMirror m = findPausableMethod(className, methodName, desc);
  54. if (m != null) {
  55. for (String ex : m.getExceptionTypes()) {
  56. if (isNonPausableClass(ex)) continue;
  57. ClassMirror c = classForName(ex);
  58. if (NOT_PAUSABLE.isAssignableFrom(c)) {
  59. return METHOD_NOT_PAUSABLE;
  60. }
  61. if (PAUSABLE.isAssignableFrom(c)) {
  62. return PAUSABLE_METHOD_FOUND;
  63. }
  64. }
  65. return METHOD_NOT_PAUSABLE;
  66. }
  67. } catch (ClassMirrorNotFoundException ignore) {
  68. }
  69. return ret;
  70. }
  71. public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
  72. className = className.replace('/', '.');
  73. return mirrors.classForName(className);
  74. }
  75. public ClassMirror[] classForNames(String[] classNames) throws ClassMirrorNotFoundException {
  76. if (classNames == null) {
  77. return new ClassMirror[0];
  78. }
  79. ClassMirror[] ret = new ClassMirror[classNames.length];
  80. int i = 0;
  81. for (String cn : classNames) {
  82. ret[i++] = classForName(cn);
  83. }
  84. return ret;
  85. }
  86. private MethodMirror findPausableMethod(String className, String methodName, String desc)
  87. throws ClassMirrorNotFoundException {
  88. if (isNonPausableClass(className) || isNonPausableMethod(methodName))
  89. return null;
  90. ClassMirror cl = classForName(className);
  91. if (cl == null) return null;
  92. for (MethodMirror om : cl.getDeclaredMethods()) {
  93. if (om.getName().equals(methodName)) {
  94. // when comparing descriptors only compare arguments, not return types
  95. String omDesc= om.getMethodDescriptor();
  96. if (omDesc.substring(0,omDesc.indexOf(")")).equals(desc.substring(0,desc.indexOf(")")))) {
  97. if (om.isBridge()) continue;
  98. return om;
  99. }
  100. }
  101. }
  102. if (OBJECT.equals(cl))
  103. return null;
  104. MethodMirror m = findPausableMethod(cl.getSuperclass(), methodName, desc);
  105. if (m != null)
  106. return m;
  107. for (String ifname : cl.getInterfaces()) {
  108. if (isNonPausableClass(ifname)) continue;
  109. m = findPausableMethod(ifname, methodName, desc);
  110. if (m != null)
  111. return m;
  112. }
  113. return null;
  114. }
  115. @SuppressWarnings("unused")
  116. private static String statusToStr(int st) {
  117. switch (st) {
  118. case METHOD_NOT_FOUND_OR_PAUSABLE:
  119. return "not found or pausable";
  120. case PAUSABLE_METHOD_FOUND:
  121. return "pausable";
  122. case METHOD_NOT_PAUSABLE:
  123. return "not pausable";
  124. default:
  125. throw new AssertionError("Unknown status");
  126. }
  127. }
  128. public String commonSuperType(String oa, String ob) throws ClassMirrorNotFoundException {
  129. String a = toClassName(oa);
  130. String b = toClassName(ob);
  131. try {
  132. ClassMirror ca = classForName(a);
  133. ClassMirror cb = classForName(b);
  134. if (ca.isAssignableFrom(cb))
  135. return oa;
  136. if (cb.isAssignableFrom(ca))
  137. return ob;
  138. if (ca.isInterface() && cb.isInterface()) {
  139. return "java/lang/Object"; // This is what the java bytecode verifier does
  140. }
  141. } catch (ClassMirrorNotFoundException e) {
  142. // try to see if the below works...
  143. }
  144. if (a.equals(b)) {
  145. return oa;
  146. }
  147. ArrayList<String> sca = getSuperClasses(a);
  148. ArrayList<String> scb = getSuperClasses(b);
  149. int lasta = sca.size() - 1;
  150. int lastb = scb.size() - 1;
  151. do {
  152. if (sca.get(lasta).equals(scb.get(lastb))) {
  153. lasta--;
  154. lastb--;
  155. } else {
  156. break;
  157. }
  158. } while (lasta >= 0 && lastb >= 0);
  159. if (sca.size() == lasta+1) {
  160. return "java/lang/Object";
  161. }
  162. return sca.get(lasta + 1).replace('.', '/');
  163. }
  164. final private static ArrayList<String> EMPTY_STRINGS = new ArrayList<String>(0);
  165. public ArrayList<String> getSuperClasses(String name) throws ClassMirrorNotFoundException {
  166. if (name == null) {
  167. return EMPTY_STRINGS;
  168. }
  169. ArrayList<String> ret = new ArrayList<String>(3);
  170. while (name != null) {
  171. ret.add(name);
  172. ClassMirror c = classForName(name);
  173. name = c.getSuperclass();
  174. }
  175. return ret;
  176. }
  177. private static String toDesc(String name) {
  178. return (name.equals(JAVA_LANG_OBJECT)) ? D_OBJECT : "L" + name.replace('.', '/') + ';';
  179. }
  180. private static String toClassName(String s) {
  181. if (s.endsWith(";"))
  182. return s.replace('/', '.').substring(1, s.length() - 1);
  183. else
  184. return s.replace('/', '.');
  185. }
  186. static String JAVA_LANG_OBJECT = "java.lang.Object";
  187. }