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.

BcelWeaver.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * Xerox/PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver.bcel;
  13. import java.io.*;
  14. import java.util.*;
  15. import java.util.zip.*;
  16. import org.apache.bcel.classfile.*;
  17. import org.apache.bcel.classfile.JavaClass;
  18. import org.aspectj.util.FileUtil;
  19. import org.aspectj.weaver.*;
  20. public class BcelWeaver implements IWeaver {
  21. private BcelWorld world;
  22. private CrosscuttingMembersSet xcutSet;
  23. public BcelWeaver(BcelWorld world) {
  24. super();
  25. this.world = world;
  26. this.xcutSet = world.getCrosscuttingMembersSet();
  27. }
  28. public BcelWeaver() {
  29. this(new BcelWorld());
  30. }
  31. // ---- fields
  32. private Map sourceJavaClasses = new HashMap(); /* String -> UnwovenClassFile */
  33. private List addedClasses = new ArrayList(); /* List<UnovenClassFile> */
  34. private List deletedTypenames = new ArrayList(); /* List<String> */
  35. private boolean needToReweaveWorld = false;
  36. private List shadowMungerList = null; // setup by prepareForWeave
  37. private List typeMungerList = null; // setup by prepareForWeave
  38. private ZipOutputStream zipOutputStream;
  39. // ----
  40. // only called for testing
  41. public void setShadowMungers(List l) {
  42. shadowMungerList = l;
  43. }
  44. public void addLibraryAspect(String aspectName) {
  45. ResolvedTypeX type = world.resolve(aspectName);
  46. System.out.println("type: " + type + " for " + aspectName);
  47. if (type.isAspect()) {
  48. xcutSet.addOrReplaceAspect(type);
  49. } else {
  50. throw new RuntimeException("unimplemented");
  51. }
  52. }
  53. public void addLibraryJarFile(File inFile) throws IOException {
  54. ZipInputStream inStream = new ZipInputStream(new FileInputStream(inFile)); //??? buffered
  55. List addedAspects = new ArrayList();
  56. while (true) {
  57. ZipEntry entry = inStream.getNextEntry();
  58. if (entry == null) break;
  59. if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
  60. continue;
  61. }
  62. ClassParser parser = new ClassParser(new ByteArrayInputStream(FileUtil.readAsByteArray(inStream)), entry.getName());
  63. JavaClass jc = parser.parse();
  64. inStream.closeEntry();
  65. ResolvedTypeX type = world.addSourceObjectType(jc);
  66. if (type.isAspect()) {
  67. addedAspects.add(type);
  68. }
  69. }
  70. inStream.close();
  71. for (Iterator i = addedAspects.iterator(); i.hasNext();) {
  72. ResolvedTypeX aspectX = (ResolvedTypeX) i.next();
  73. xcutSet.addOrReplaceAspect(aspectX);
  74. }
  75. }
  76. /** Adds all class files in the jar
  77. */
  78. public void addJarFile(File inFile, File outDir) throws IOException {
  79. needToReweaveWorld = true;
  80. //System.err.println("adding jar: " + inFile);
  81. ZipInputStream inStream = new ZipInputStream(new FileInputStream(inFile)); //??? buffered
  82. while (true) {
  83. ZipEntry entry = inStream.getNextEntry();
  84. if (entry == null) break;
  85. if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
  86. continue; //??? need to pass other things along untouched
  87. // outStream.putNextEntry(entry);
  88. // outStream.write(Utility.getByteArray(inStream));
  89. // outStream.closeEntry();
  90. // return;
  91. }
  92. //System.err.println("adding class: " + entry.getName());
  93. byte[] bytes = FileUtil.readAsByteArray(inStream);
  94. String filename = entry.getName();
  95. UnwovenClassFile classFile = new UnwovenClassFile(new File(outDir, filename).getAbsolutePath(), bytes);
  96. inStream.closeEntry();
  97. this.addClassFile(classFile);
  98. }
  99. inStream.close();
  100. }
  101. /** Should be addOrReplace
  102. */
  103. public void addClassFile(UnwovenClassFile classFile) {
  104. addedClasses.add(classFile);
  105. sourceJavaClasses.put(classFile.getClassName(), classFile);
  106. world.addSourceObjectType(classFile.getJavaClass());
  107. }
  108. public void deleteClassFile(String typename) {
  109. deletedTypenames.add(typename);
  110. sourceJavaClasses.remove(typename);
  111. world.deleteSourceObjectType(TypeX.forName(typename));
  112. }
  113. // ---- weave preparation
  114. public void prepareForWeave() {
  115. needToReweaveWorld = false;
  116. // update mungers
  117. for (Iterator i = addedClasses.iterator(); i.hasNext(); ) {
  118. UnwovenClassFile jc = (UnwovenClassFile)i.next();
  119. String name = jc.getClassName();
  120. ResolvedTypeX type = world.resolve(name);
  121. if (type.isAspect()) {
  122. needToReweaveWorld |= xcutSet.addOrReplaceAspect(type);
  123. }
  124. }
  125. for (Iterator i = deletedTypenames.iterator(); i.hasNext(); ) {
  126. String name = (String)i.next();
  127. xcutSet.deleteAspect(TypeX.forName(name));
  128. needToReweaveWorld = true;
  129. }
  130. shadowMungerList = xcutSet.getShadowMungers();
  131. typeMungerList = xcutSet.getTypeMungers();
  132. //XXX this gets us a stable (but completely meaningless) order
  133. Collections.sort(
  134. shadowMungerList,
  135. new Comparator() {
  136. public int compare(Object o1, Object o2) {
  137. return o1.toString().compareTo(o2.toString());
  138. }
  139. });
  140. }
  141. public void dumpUnwoven(File file) throws IOException {
  142. BufferedOutputStream os = FileUtil.makeOutputStream(file);
  143. this.zipOutputStream = new ZipOutputStream(os);
  144. dumpUnwoven();
  145. zipOutputStream.close(); //this flushes and closes the acutal file
  146. }
  147. public void dumpUnwoven() throws IOException {
  148. Collection filesToDump = new HashSet(sourceJavaClasses.values());
  149. for (Iterator i = filesToDump.iterator(); i.hasNext(); ) {
  150. UnwovenClassFile classFile = (UnwovenClassFile)i.next();
  151. dumpUnchanged(classFile);
  152. }
  153. }
  154. // ---- weaving
  155. public Collection weave(File file) throws IOException {
  156. OutputStream os = FileUtil.makeOutputStream(file);
  157. this.zipOutputStream = new ZipOutputStream(os);
  158. Collection c = weave();
  159. zipOutputStream.close(); //this flushes and closes the acutal file
  160. return c;
  161. }
  162. public Collection weave() throws IOException {
  163. prepareForWeave();
  164. Collection filesToWeave;
  165. if (needToReweaveWorld) {
  166. filesToWeave = sourceJavaClasses.values();
  167. } else {
  168. filesToWeave = addedClasses;
  169. }
  170. Collection wovenClassNames = new ArrayList();
  171. //XXX this isn't quite the right place for this...
  172. for (Iterator i = filesToWeave.iterator(); i.hasNext(); ) {
  173. UnwovenClassFile classFile = (UnwovenClassFile)i.next();
  174. String className = classFile.getClassName();
  175. ResolvedTypeX onType = world.resolve(className);
  176. weave(onType);
  177. }
  178. // first weave into aspects
  179. for (Iterator i = filesToWeave.iterator(); i.hasNext(); ) {
  180. UnwovenClassFile classFile = (UnwovenClassFile)i.next();
  181. String className = classFile.getClassName();
  182. BcelObjectType classType = (BcelObjectType) world.resolve(className);
  183. if (classType.isAspect()) {
  184. weave(classFile, classType);
  185. wovenClassNames.add(className);
  186. }
  187. }
  188. // then weave into non-aspects
  189. for (Iterator i = filesToWeave.iterator(); i.hasNext(); ) {
  190. UnwovenClassFile classFile = (UnwovenClassFile)i.next();
  191. String className = classFile.getClassName();
  192. BcelObjectType classType = (BcelObjectType) world.resolve(className);
  193. if (! classType.isAspect()) {
  194. weave(classFile, classType);
  195. wovenClassNames.add(className);
  196. }
  197. }
  198. if (zipOutputStream != null && !needToReweaveWorld) {
  199. Collection filesToDump = new HashSet(sourceJavaClasses.values());
  200. filesToDump.removeAll(filesToWeave);
  201. for (Iterator i = filesToDump.iterator(); i.hasNext(); ) {
  202. UnwovenClassFile classFile = (UnwovenClassFile)i.next();
  203. dumpUnchanged(classFile);
  204. }
  205. }
  206. addedClasses = new ArrayList();
  207. deletedTypenames = new ArrayList();
  208. return wovenClassNames;
  209. }
  210. private void weave(ResolvedTypeX onType) {
  211. onType.clearInterTypeMungers();
  212. for (Iterator i = typeMungerList.iterator(); i.hasNext(); ) {
  213. ConcreteTypeMunger m = (ConcreteTypeMunger)i.next();
  214. if (m.matches(onType)) {
  215. onType.addInterTypeMunger(m);
  216. }
  217. }
  218. }
  219. // non-private for testing
  220. LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType) throws IOException {
  221. JavaClass javaClass = classType.getJavaClass();
  222. List shadowMungers = fastMatch(shadowMungerList, javaClass);
  223. List typeMungers = fastMatch(classType.getInterTypeMungers(), javaClass);
  224. LazyClassGen clazz = null;
  225. if (shadowMungers.size() > 0 || typeMungers.size() > 0 || classType.isAspect()) {
  226. clazz = classType.getLazyClassGen();
  227. try {
  228. boolean isChanged = BcelClassWeaver.weave(world, clazz, shadowMungers, typeMungers);
  229. if (isChanged) {
  230. dump(classFile, clazz);
  231. return clazz;
  232. }
  233. } catch (RuntimeException re) {
  234. System.err.println("trouble in: ");
  235. clazz.print(System.err);
  236. throw re;
  237. } catch (Error re) {
  238. System.err.println("trouble in: ");
  239. clazz.print(System.err);
  240. throw re;
  241. }
  242. }
  243. dumpUnchanged(classFile);
  244. return clazz;
  245. }
  246. // ---- writing
  247. private void dumpUnchanged(UnwovenClassFile classFile) throws IOException {
  248. if (zipOutputStream != null) {
  249. writeZipEntry(getEntryName(classFile.getJavaClass().getClassName()), classFile.getBytes());
  250. } else {
  251. classFile.writeUnchangedBytes();
  252. }
  253. }
  254. private String getEntryName(String className) {
  255. //XXX what does bcel's getClassName do for inner names
  256. return className.replace('.', '/') + ".class";
  257. }
  258. private void dump(UnwovenClassFile classFile, LazyClassGen clazz) throws IOException {
  259. if (zipOutputStream != null) {
  260. String mainClassName = classFile.getJavaClass().getClassName();
  261. writeZipEntry(getEntryName(mainClassName),
  262. clazz.getJavaClass().getBytes());
  263. if (!clazz.getChildClasses().isEmpty()) {
  264. for (Iterator i = clazz.getChildClasses().iterator(); i.hasNext();) {
  265. UnwovenClassFile.ChildClass c = (UnwovenClassFile.ChildClass) i.next();
  266. writeZipEntry(getEntryName(mainClassName + "$" + c.name), c.bytes);
  267. }
  268. }
  269. } else {
  270. classFile.writeWovenBytes(
  271. clazz.getJavaClass().getBytes(),
  272. clazz.getChildClasses()
  273. );
  274. }
  275. }
  276. private void writeZipEntry(String name, byte[] bytes) throws IOException {
  277. ZipEntry newEntry = new ZipEntry(name); //??? get compression scheme right
  278. zipOutputStream.putNextEntry(newEntry);
  279. zipOutputStream.write(bytes);
  280. zipOutputStream.closeEntry();
  281. }
  282. // ---- fast matching
  283. // boolean fastMatch(JavaClass jc) {
  284. // ConstantPool pool = jc.getConstantPool();
  285. // for (int i=0, len=pool.getLength(); i < len; i++) {
  286. // Constant c = pool.getConstant(i);
  287. // if (c instanceof ConstantNameAndType) {
  288. // ConstantNameAndType nt = (ConstantNameAndType)c;
  289. // if (nt.getName(pool).equals("toShortString")) {
  290. // //System.out.println("found in " + jc);
  291. // return true;
  292. // }
  293. // }
  294. // }
  295. // return false;
  296. // }
  297. //XXX need to implement a real fast-match here
  298. private List fastMatch(List list, JavaClass javaClass) {
  299. if (list == null) return Collections.EMPTY_LIST;
  300. return list;
  301. }
  302. }