/src/kilim/mirrors/CachedClassMirrors.java

http://github.com/kilim/kilim · Java · 287 lines · 219 code · 56 blank · 12 comment · 26 complexity · 3f30a903338b3fa857bd281535ef7b70 MD5 · raw file

  1. // copyright 2016 nqzero, 2014 sriram srinivasan - offered under the terms of the MIT License
  2. package kilim.mirrors;
  3. import java.io.DataInputStream;
  4. import java.io.IOException;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import java.util.concurrent.ConcurrentHashMap;
  8. import kilim.Constants;
  9. import kilim.WeavingClassLoader;
  10. import org.objectweb.asm.AnnotationVisitor;
  11. import org.objectweb.asm.Attribute;
  12. import org.objectweb.asm.ClassReader;
  13. import org.objectweb.asm.ClassVisitor;
  14. import org.objectweb.asm.FieldVisitor;
  15. import org.objectweb.asm.MethodVisitor;
  16. import org.objectweb.asm.Opcodes;
  17. /**
  18. * CachedClassMirrors caches information about a set of classes that are loaded through byte arrays, and which
  19. * are not already loaded by the classloader
  20. **/
  21. public class CachedClassMirrors {
  22. final static String[] EMPTY_SET = new String[0];
  23. ConcurrentHashMap<String,ClassMirror> cachedClasses = new ConcurrentHashMap<String, ClassMirror>();
  24. final ClassLoader source;
  25. public CachedClassMirrors() {
  26. source = getClass().getClassLoader();
  27. }
  28. public CachedClassMirrors(ClassLoader $source) {
  29. source = $source;
  30. }
  31. public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
  32. // defer to loaded class objects first, then to cached class mirrors.
  33. ClassMirror ret = cachedClasses.get(className);
  34. if (ret != null) return ret;
  35. // even if a class is loaded, we can't tell if it's resolved, so querying it might trigger
  36. // loading of other classes, so use asm for everything
  37. byte [] code = WeavingClassLoader.findCode(source,className);
  38. if (code != null) return mirror(code);
  39. throw new ClassMirrorNotFoundException(className);
  40. }
  41. public ClassMirror mirror(byte[] bytecode) {
  42. ClassMirror mirror = new ClassMirror(bytecode);
  43. return place(mirror);
  44. }
  45. private ClassMirror place(ClassMirror r1) {
  46. r1.mirrors = this;
  47. ClassMirror r2 = cachedClasses.putIfAbsent(r1.getName(),r1);
  48. return r2==null ? r1:r2;
  49. }
  50. /** get the major version of klass by loading the bytecode from source, unused but useful for debugging */
  51. public static int getVersion(ClassLoader source,Class klass) {
  52. String cname = WeavingClassLoader.makeResourceName(klass.getName());
  53. DataInputStream in = new DataInputStream(source.getResourceAsStream(cname));
  54. try {
  55. int magic = in.readInt();
  56. int minor = in.readUnsignedShort();
  57. int major = in.readUnsignedShort();
  58. in.close();
  59. return major;
  60. }
  61. catch (IOException ex) { throw new RuntimeException(ex); }
  62. }
  63. public ClassMirror mirror(Class<?> clazz) {
  64. try {
  65. return classForName(clazz.getName());
  66. }
  67. catch (ClassMirrorNotFoundException ex) {
  68. throw new AssertionError("class-based lookup should never fail",ex);
  69. }
  70. }
  71. private static String map(String word) {
  72. return word==null ? null : word.replace("/",".");
  73. }
  74. private static String [] map(String [] words) {
  75. if (words==null) return words;
  76. String [] mod = new String[words.length];
  77. for (int ii = 0; ii < mod.length; ii++) mod[ii] = words[ii].replace("/",".");
  78. return mod;
  79. }
  80. public static class ClassMirror {
  81. private String name;
  82. private boolean isInterface;
  83. private MethodMirror[] declaredMethods;
  84. private String[] interfaceNames;
  85. private String superName;
  86. private int version = 0;
  87. CachedClassMirrors mirrors;
  88. private List<MethodMirror> tmpMethodList; //used only while processing bytecode.
  89. public ClassMirror(byte []bytecode) {
  90. ClassReader cr = new ClassReader(bytecode);
  91. Visitor visitor = new Visitor();
  92. cr.accept(visitor, /*flags*/0);
  93. }
  94. // used by DualMirror (external package) for testing the mirrors
  95. ClassMirror() {}
  96. public String getName() {
  97. return name;
  98. }
  99. public boolean isInterface() {
  100. return isInterface;
  101. }
  102. public boolean equals(Object obj) {
  103. if (obj instanceof ClassMirror) {
  104. ClassMirror mirr = (ClassMirror) obj;
  105. String n1 = name, n2 = mirr.getName();
  106. return n1.equals(n2) && mirr.isInterface() == this.isInterface;
  107. }
  108. return false;
  109. }
  110. public int hashCode() {
  111. return this.name.hashCode();
  112. }
  113. public MethodMirror[] getDeclaredMethods() {
  114. if (declaredMethods != null)
  115. return declaredMethods;
  116. return declaredMethods = new MethodMirror[0];
  117. }
  118. public String[] getInterfaces() throws ClassMirrorNotFoundException {
  119. return interfaceNames;
  120. }
  121. public String getSuperclass() throws ClassMirrorNotFoundException {
  122. return superName;
  123. }
  124. public int version() {
  125. return (version & 0x00FF);
  126. }
  127. public boolean isAssignableFrom(ClassMirror c) throws ClassMirrorNotFoundException {
  128. if (c==null) return false;
  129. if (this.equals(c)) return true;
  130. String sname = c.getSuperclass();
  131. ClassMirror supcl = sname==null ? null : mirrors.classForName(sname);
  132. if (isAssignableFrom(supcl)) return true;
  133. for (String icl: c.getInterfaces()) {
  134. supcl = mirrors.classForName(icl);
  135. if (isAssignableFrom(supcl))
  136. return true;
  137. }
  138. return false;
  139. }
  140. public class Visitor extends ClassVisitor {
  141. Visitor() {
  142. super(Constants.KILIM_ASM);
  143. }
  144. // ClassVisitor implementation
  145. public void visit(int $version, int access, String $name, String signature, String $superName,
  146. String[] $interfaces) {
  147. version = $version;
  148. name = map($name);
  149. superName = map($superName);
  150. interfaceNames = $interfaces == null ? CachedClassMirrors.EMPTY_SET : map($interfaces);
  151. isInterface = (access & Opcodes.ACC_INTERFACE) > 0;
  152. if (isInterface) superName = null;
  153. }
  154. public MethodVisitor visitMethod(int access, String name, String desc, String signature,
  155. String[] exceptions) {
  156. if (name.equals("<init>")) return null;
  157. if (name.equals("<clinit>")) return null;
  158. if (tmpMethodList == null) {
  159. tmpMethodList = new ArrayList<CachedClassMirrors.MethodMirror>();
  160. }
  161. MethodMirror mirror = new MethodMirror(access, name, desc, map(exceptions));
  162. tmpMethodList.add(mirror);
  163. return null; // null MethodVisitor to avoid examining the instructions.
  164. }
  165. public void visitEnd() {
  166. if (tmpMethodList != null) {
  167. declaredMethods = new MethodMirror[tmpMethodList.size()];
  168. int i = 0;
  169. for (MethodMirror mm: tmpMethodList) {
  170. declaredMethods[i++] = mm;
  171. }
  172. tmpMethodList = null;
  173. }
  174. }
  175. // Dummy methods
  176. public void visitSource(String source, String debug) {}
  177. public void visitNestMember(String nestMember) {}
  178. public void visitNestHost(String nestHost) {}
  179. public void visitOuterClass(String owner, String name, String desc) {}
  180. public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
  181. return DummyAnnotationVisitor.singleton;
  182. }
  183. public void visitAttribute(Attribute attr) {}
  184. public void visitInnerClass(String name, String outerName, String innerName, int access) {}
  185. public FieldVisitor visitField(int access, String name, String desc, String signature,
  186. Object value) {
  187. return null;
  188. }
  189. }
  190. }
  191. static class DummyAnnotationVisitor extends AnnotationVisitor {
  192. public DummyAnnotationVisitor() {
  193. super(Constants.KILIM_ASM);
  194. }
  195. static DummyAnnotationVisitor singleton = new DummyAnnotationVisitor();
  196. public void visit(String name, Object value) {}
  197. public AnnotationVisitor visitAnnotation(String name, String desc) {return this;}
  198. public AnnotationVisitor visitArray(String name) {return DummyAnnotationVisitor.singleton;}
  199. public void visitEnd() {}
  200. public void visitEnum(String name, String desc, String value) {}
  201. }
  202. public static class MethodMirror {
  203. private String[] exceptions;
  204. private String desc;
  205. private String name;
  206. private int modifiers;
  207. private boolean isBridge;
  208. public MethodMirror(int modifiers, String name, String desc, String[] exceptions) {
  209. this.modifiers = modifiers;
  210. this.name = name;
  211. this.desc = desc;
  212. this.exceptions = (exceptions == null) ? CachedClassMirrors.EMPTY_SET : exceptions;
  213. isBridge = (modifiers & Opcodes.ACC_BRIDGE) > 0;
  214. }
  215. public String getName() {
  216. return name;
  217. }
  218. public String[] getExceptionTypes() {
  219. return exceptions;
  220. }
  221. public String getMethodDescriptor() {
  222. return desc;
  223. }
  224. public boolean isBridge() {
  225. return isBridge;
  226. }
  227. public int getModifiers() {
  228. return modifiers;
  229. }
  230. }
  231. }