/src/kilim/analysis/ClassWeaver.java

http://github.com/kilim/kilim · Java · 306 lines · 202 code · 39 blank · 65 comment · 48 complexity · c05c72800ba09a2ad08aa74d8e3c147e 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.analysis;
  7. import static kilim.Constants.STATE_CLASS;
  8. import static kilim.Constants.WOVEN_FIELD;
  9. import static org.objectweb.asm.Opcodes.ACC_FINAL;
  10. import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
  11. import static org.objectweb.asm.Opcodes.ACC_STATIC;
  12. import static org.objectweb.asm.Opcodes.ALOAD;
  13. import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
  14. import static org.objectweb.asm.Opcodes.RETURN;
  15. import static org.objectweb.asm.Opcodes.V1_1;
  16. import java.io.IOException;
  17. import java.io.InputStream;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.LinkedList;
  21. import java.util.List;
  22. import kilim.Constants;
  23. import kilim.KilimException;
  24. import kilim.mirrors.Detector;
  25. import org.objectweb.asm.Attribute;
  26. import org.objectweb.asm.ClassVisitor;
  27. import org.objectweb.asm.ClassWriter;
  28. import org.objectweb.asm.MethodVisitor;
  29. import org.objectweb.asm.tree.AnnotationNode;
  30. import org.objectweb.asm.tree.FieldNode;
  31. import org.objectweb.asm.tree.InnerClassNode;
  32. /**
  33. * This class is the main entry point for the Weave tool. It uses
  34. * ClassFlow to parse and analyze a class file, and writes out a
  35. * CPS transformed file if needed
  36. */
  37. public class ClassWeaver {
  38. public ClassFlow classFlow;
  39. List<ClassInfo> classInfoList = new LinkedList<ClassInfo>();
  40. static ThreadLocal<HashMap<String, ClassInfo>> stateClasses_ =
  41. new ThreadLocal<HashMap<String, ClassInfo>>() {
  42. protected HashMap<String, ClassInfo> initialValue() {
  43. return new HashMap<String, ClassInfo>();
  44. }
  45. };
  46. public static void reset() {
  47. stateClasses_.set(new HashMap<String, ClassInfo>() );
  48. }
  49. public KilimContext context;
  50. public ClassWeaver(KilimContext context,InputStream is) throws IOException {
  51. this.context = context;
  52. classFlow = new ClassFlow(context,is);
  53. }
  54. public void weave() throws KilimException {
  55. classFlow.analyze(false);
  56. if (needsWeaving() && classFlow.isPausable()) {
  57. boolean computeFrames = (classFlow.version & 0x00FF) >= 50;
  58. ClassWriter cw = new kilim.analysis.ClassWriter(
  59. computeFrames ? ClassWriter.COMPUTE_FRAMES : 0, context.detector);
  60. accept(cw);
  61. addClassInfo(new ClassInfo(classFlow.getClassName(), cw.toByteArray()));
  62. }
  63. }
  64. private void accept(final ClassVisitor cv) {
  65. ClassFlow cf = classFlow;
  66. // visits header
  67. String[] interfaces = toStringArray(cf.interfaces);
  68. cv.visit(cf.version, cf.access, cf.name, cf.signature, cf.superName, interfaces);
  69. // visits source
  70. if (cf.sourceFile != null || cf.sourceDebug != null) {
  71. cv.visitSource(cf.sourceFile, cf.sourceDebug);
  72. }
  73. if (cf.nestHostClass != null)
  74. cv.visitNestHost(cf.nestHostClass);
  75. // visits outer class
  76. if (cf.outerClass != null) {
  77. cv.visitOuterClass(cf.outerClass, cf.outerMethod, cf.outerMethodDesc);
  78. }
  79. if (cf.nestMembers != null)
  80. for (String member : cf.nestMembers)
  81. cv.visitNestMember(member);
  82. // visits attributes and annotations
  83. int i, n;
  84. AnnotationNode an;
  85. n = cf.visibleAnnotations == null ? 0 : cf.visibleAnnotations.size();
  86. for (i = 0; i < n; ++i) {
  87. an = (AnnotationNode) cf.visibleAnnotations.get(i);
  88. an.accept(cv.visitAnnotation(an.desc, true));
  89. }
  90. n = cf.invisibleAnnotations == null ? 0
  91. : cf.invisibleAnnotations.size();
  92. for (i = 0; i < n; ++i) {
  93. an = (AnnotationNode) cf.invisibleAnnotations.get(i);
  94. an.accept(cv.visitAnnotation(an.desc, false));
  95. }
  96. n = cf.attrs == null ? 0 : cf.attrs.size();
  97. for (i = 0; i < n; ++i) {
  98. cv.visitAttribute((Attribute) cf.attrs.get(i));
  99. }
  100. // visits inner classes
  101. for (i = 0; i < cf.innerClasses.size(); ++i) {
  102. ((InnerClassNode) cf.innerClasses.get(i)).accept(cv);
  103. }
  104. // visits fields
  105. for (i = 0; i < cf.fields.size(); ++i) {
  106. ((FieldNode) cf.fields.get(i)).accept(cv);
  107. }
  108. /*
  109. * Mark this class as "processed" by adding a dummy field, so that
  110. * we don't weave an already woven file
  111. */
  112. cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, WOVEN_FIELD, "Z", "Z", Boolean.TRUE);
  113. // visits methods
  114. MethodFlow sam = classFlow.getSAM();
  115. for (i = 0; i < cf.methods.size(); ++i) {
  116. MethodFlow m = (MethodFlow) cf.methods.get(i);
  117. if (needsWeaving(m)) {
  118. // For pausable methods, we generate two function signatures, one the original
  119. // and the other with a fiber as the last param. For a non-abstract method, the
  120. // fiber'd version gets the original's body, and the original version throws
  121. // an error saying "Not Woven", in case it is called at run time. This latter part
  122. // is done in makeNotWovenMethod below.
  123. // However, if it is a single abstract method (SAM) of a functional interface,
  124. // then we invert the arrangement. We generate two method bodies as before,
  125. // but the fiber'd version gets the "not woven" message. This preserves the
  126. // original method as the SAM. However, the weaver (see CallWeaver and SAMWeaver)
  127. // arranges it such that the invokedynamic instruction bridges the fiber'd version
  128. // of the method with the body of the lambda expression.
  129. boolean isSAM = (sam == m);
  130. MethodWeaver mw = new MethodWeaver(this, context.detector, m, isSAM);
  131. mw.accept(cv);
  132. if (m.isPausable())
  133. mw.makeNotWovenMethod(cv, m, isSAM);
  134. } else {
  135. m.restoreNonInstructionNodes();
  136. m.accept(cv);
  137. }
  138. }
  139. // create shim methods (if any) to wrap SAM interface methods
  140. for (SAMweaver sw: samWeavers) {
  141. sw.accept(cv);
  142. }
  143. // visits end
  144. cv.visitEnd();
  145. }
  146. @SuppressWarnings(value = { "unchecked" })
  147. static String[] toStringArray(List list) {
  148. String[] array = new String[list.size()];
  149. list.toArray(array);
  150. return array;
  151. }
  152. void addClassInfo(ClassInfo ci) {
  153. classInfoList.add(ci);
  154. }
  155. public List<ClassInfo> getClassInfos() {
  156. return classInfoList;
  157. }
  158. /*
  159. * A method needs weaving ordinarily if it is marked pausable.
  160. * However, if there exists another method with the same name
  161. * and parameters and an additional Fiber parameter as the last
  162. * one, then this method doesn't need weaving. Examples are
  163. * kilim.Task.yield and kilim.Task.sleep
  164. */
  165. boolean needsWeaving(MethodFlow mf) {
  166. if (!mf.needsWeaving() || mf.desc.endsWith(Constants.D_FIBER_LAST_ARG))
  167. return false;
  168. String fdesc = mf.desc.replace(")", Constants.D_FIBER_LAST_ARG);
  169. for (MethodFlow omf: classFlow.getMethodFlows()) {
  170. if (omf == mf) continue;
  171. if (mf.name.equals(omf.name) && fdesc.equals(omf.desc)) {
  172. return false;
  173. }
  174. }
  175. return true;
  176. }
  177. boolean needsWeaving() {
  178. if (classFlow.isWoven) return false;
  179. for (MethodFlow mf: classFlow.getMethodFlows()) {
  180. if (needsWeaving(mf)) return true;
  181. }
  182. return false;
  183. }
  184. /**
  185. * Create a custom class (structure) to hold the state. The name of the
  186. * state reflects the numbers of the various VMtypes in valInfoList. class
  187. * kilim.SO2IJ3 reflects a class that stores two Objects one Integer and 3
  188. * longs.
  189. *
  190. * <pre>
  191. * class kilim.SO2IJ3 extends kilim.State {
  192. * public Object f1, f2;
  193. * public int f3;
  194. * public long f4, f5, f6;
  195. * }
  196. * </pre>
  197. *
  198. * If there's no data to store, we use the kilim.State class directly to
  199. * store the basic amount of data necessary to restore the stack.
  200. */
  201. String createStateClass(ValInfoList valInfoList) {
  202. int numByType[] = { 0, 0, 0, 0, 0 };
  203. for (ValInfo vi : valInfoList) {
  204. numByType[vi.vmt]++;
  205. }
  206. String className = makeClassName(numByType);
  207. ClassInfo classInfo= null;
  208. classInfo= stateClasses_.get().get(className);
  209. if (classInfo == null) {
  210. ClassWriter cw = new kilim.analysis.ClassWriter(ClassWriter.COMPUTE_FRAMES, context.detector);
  211. cw.visit(V1_1, ACC_PUBLIC | ACC_FINAL, className, null, "kilim/State", null);
  212. // Create default constructor
  213. // <init>() {
  214. // super(); // call java/lang/Object.<init>()
  215. // }
  216. MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  217. mw.visitVarInsn(ALOAD, 0);
  218. mw.visitMethodInsn(INVOKESPECIAL, STATE_CLASS, "<init>", "()V", false);
  219. mw.visitInsn(RETURN);
  220. // this code uses a maximum of one stack element and one local variable
  221. mw.visitMaxs(1, 1);
  222. mw.visitEnd();
  223. // create fields of the appropriate type.
  224. for (ValInfo vi : valInfoList) {
  225. cw.visitField(ACC_PUBLIC, vi.fieldName, vi.fieldDesc(), null, null);
  226. }
  227. classInfo= new ClassInfo(className, cw.toByteArray());
  228. stateClasses_.get().put(className, classInfo);
  229. }
  230. if (!classInfoList.contains(classInfo))
  231. addClassInfo(classInfo);
  232. return className;
  233. }
  234. private String makeClassName(int[] numByType) {
  235. StringBuilder sb = new StringBuilder(30);
  236. sb.append("kilim/S_");
  237. for (int t = 0; t < 5; t++) {
  238. int c = numByType[t];
  239. if (c == 0)
  240. continue;
  241. sb.append(VMType.abbrev[t]);
  242. if (c > 1) {
  243. sb.append(c);
  244. }
  245. }
  246. return sb.toString();
  247. }
  248. boolean isInterface() {
  249. return classFlow.isInterface();
  250. }
  251. ArrayList<SAMweaver> samWeavers = new ArrayList<SAMweaver>();
  252. SAMweaver getSAMWeaver(String owner, String methodName, String desc, boolean itf) {
  253. SAMweaver sw = new SAMweaver(context,owner, methodName, desc, itf);
  254. // intern
  255. for (SAMweaver s: samWeavers) {
  256. if (s.equals(sw)) {
  257. return s;
  258. }
  259. }
  260. samWeavers.add(sw);
  261. sw.setIndex(samWeavers.size());
  262. return sw;
  263. }
  264. String getName() {
  265. return classFlow.name;
  266. }
  267. }