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.

WeaverStateInfo.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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 Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver;
  13. import java.io.ByteArrayInputStream;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.DataInputStream;
  16. import java.io.DataOutputStream;
  17. import java.io.IOException;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Set;
  25. import java.util.zip.ZipEntry;
  26. import java.util.zip.ZipInputStream;
  27. import org.aspectj.bridge.IMessage;
  28. import org.aspectj.weaver.bcel.BcelTypeMunger;
  29. /**
  30. * WeaverStateInfo represents how a type was processed. It is used by the weaver to determine how a type
  31. * was previously treated and whether reweaving is allowed.
  32. * The format in the data stream is:
  33. *
  34. * Byte: Kind. UNTOUCHED|WOVEN|EXTENDED - If extended it can have two extra bits set 'REWEAVABLE' and 'REWEAVABLE_COMPRESSION_BIT'
  35. * Short: typeMungerCount - how many type mungers have affected this type
  36. * <UnresolvedType & ResolvedTypeMunger>: The type mungers themselves
  37. * If we are reweavable then we also have:
  38. * Short: Number of aspects that touched this type in some way when it was previously woven
  39. * <String> The fully qualified name of each type
  40. * Int: Length of class file data (i.e. the unwovenclassfile)
  41. * Byte[]: The class file data, compressed if REWEAVABLE_COMPRESSION_BIT set.
  42. */
  43. public class WeaverStateInfo {
  44. private List/*Entry*/ typeMungers;
  45. private boolean oldStyle;
  46. private boolean reweavable;
  47. private boolean reweavableCompressedMode; // If true, unwovenClassFile is uncompressed on read
  48. private boolean reweavableDiffMode; // if true, unwovenClassFile is written and read as a diff
  49. private Set /*String*/ aspectsAffectingType; // These must exist in the world for reweaving to be valid
  50. private byte[] unwovenClassFile; // Original 'untouched' class file
  51. private static boolean reweavableDefault = true; // ajh02: changed from false;
  52. private static boolean reweavableCompressedModeDefault = false;
  53. private static boolean reweavableDiffModeDefault = true;
  54. // when serializing the WeaverStateInfo we come to adding the reweavable data,
  55. // we'd like to add a diff of the unwovenClassFile and the wovenClassFile,
  56. // but we don't have the wovenClassFile yet as we're still in the process of making it.
  57. // so we put this key there instead as a stub.
  58. // Then when the wovenClassFile has been made, replaceKeyWithDiff is called.
  59. private static byte [] key = {
  60. -51, 34, 105, 56, -34, 65, 45, 78, -26, 125, 114, 97, 98, 1, -1, -42
  61. };
  62. private boolean unwovenClassFileIsADiff = false;
  63. public WeaverStateInfo() {
  64. this(new ArrayList(), false,reweavableDefault,reweavableCompressedModeDefault,reweavableDiffModeDefault);
  65. }
  66. public WeaverStateInfo(boolean reweavable) {
  67. this(new ArrayList(), false,reweavable,reweavableCompressedModeDefault,reweavableDiffModeDefault);
  68. }
  69. private WeaverStateInfo(List typeMungers, boolean oldStyle,boolean reweavableMode,boolean reweavableCompressedMode,boolean reweavableDiffMode) {
  70. this.typeMungers = typeMungers;
  71. this.oldStyle = oldStyle;
  72. this.reweavable = reweavableMode;
  73. this.reweavableCompressedMode = reweavableCompressedMode;
  74. this.reweavableDiffMode = reweavableMode?reweavableDiffMode:false;
  75. this.aspectsAffectingType= new HashSet();
  76. this.unwovenClassFile = null;
  77. }
  78. public static void setReweavableModeDefaults(boolean mode, boolean compress, boolean diff) {
  79. reweavableDefault = mode;
  80. reweavableCompressedModeDefault = compress;
  81. reweavableDiffModeDefault = diff;
  82. }
  83. private static final int UNTOUCHED=0, WOVEN=2, EXTENDED=3;
  84. // Use 'bits' for these capabilities - only valid in EXTENDED mode
  85. private static final byte REWEAVABLE_BIT = 1<<4;
  86. private static final byte REWEAVABLE_COMPRESSION_BIT = 1<<5;
  87. private static final byte REWEAVABLE_DIFF_BIT = 1<<6;
  88. /** See comments on write() */
  89. public static final WeaverStateInfo read(VersionedDataInputStream s, ISourceContext context) throws IOException {
  90. byte b = s.readByte();
  91. boolean isReweavable = ((b&REWEAVABLE_BIT)!=0);
  92. if (isReweavable){
  93. b=(byte) (b-REWEAVABLE_BIT);
  94. }
  95. boolean isReweavableCompressed = ((b&REWEAVABLE_COMPRESSION_BIT)!=0);
  96. if (isReweavableCompressed){
  97. b=(byte) (b-REWEAVABLE_COMPRESSION_BIT);
  98. }
  99. boolean isReweavableDiff = ((b&REWEAVABLE_DIFF_BIT)!=0);
  100. if (isReweavableDiff){
  101. b=(byte) (b-REWEAVABLE_DIFF_BIT);
  102. }
  103. switch(b) {
  104. case UNTOUCHED:
  105. throw new RuntimeException("unexpected UNWOVEN");
  106. case WOVEN:
  107. return new WeaverStateInfo(Collections.EMPTY_LIST, true,isReweavable,isReweavableCompressed,isReweavableDiff);
  108. case EXTENDED:
  109. int n = s.readShort();
  110. List l = new ArrayList();
  111. for (int i=0; i < n; i++) {
  112. UnresolvedType aspectType = UnresolvedType.read(s);
  113. ResolvedTypeMunger typeMunger =
  114. ResolvedTypeMunger.read(s, context);
  115. l.add(new Entry(aspectType, typeMunger));
  116. }
  117. WeaverStateInfo wsi = new WeaverStateInfo(l,false,isReweavable,isReweavableCompressed,isReweavableDiff);
  118. readAnyReweavableData(wsi,s);
  119. return wsi;
  120. }
  121. throw new RuntimeException("bad WeaverState.Kind: " + b+". File was :"+(context==null?"unknown":context.makeSourceLocation(0,0).toString()));
  122. }
  123. private static class Entry {
  124. public UnresolvedType aspectType;
  125. public ResolvedTypeMunger typeMunger;
  126. public Entry(UnresolvedType aspectType, ResolvedTypeMunger typeMunger) {
  127. this.aspectType = aspectType;
  128. this.typeMunger = typeMunger;
  129. }
  130. public String toString() {
  131. return "<" + aspectType + ", " + typeMunger + ">";
  132. }
  133. }
  134. /**
  135. * Serialize the WeaverStateInfo. Various bits are set within the 'kind' flag to indicate the structure of
  136. * the attribute. In reweavable diff mode a 'marker' is inserted at the start of the attribute to indicate
  137. * where the final calculated diff should be inserted. When the key is replaced with the diff, the 'kind'
  138. * byte moves to the front of the attribute - thats why in the read logic you'll see it expecting the
  139. * kind as the first byte.
  140. */
  141. public void write(DataOutputStream s) throws IOException {
  142. if (oldStyle || reweavableCompressedMode) {
  143. throw new RuntimeException("shouldn't be writing this");
  144. }
  145. byte weaverStateInfoKind = EXTENDED;
  146. if (reweavable) weaverStateInfoKind |= REWEAVABLE_BIT;
  147. if (reweavableDiffMode){
  148. s.write(key); // put key in so we can replace it with the diff later
  149. weaverStateInfoKind |= REWEAVABLE_DIFF_BIT;
  150. }
  151. s.writeByte(weaverStateInfoKind);
  152. int n = typeMungers.size();
  153. s.writeShort(n);
  154. for (int i=0; i < n; i++) {
  155. Entry e = (Entry)typeMungers.get(i);
  156. e.aspectType.write(s);
  157. e.typeMunger.write(s);
  158. }
  159. writeAnyReweavableData(this,s);
  160. }
  161. public void addConcreteMunger(ConcreteTypeMunger munger) {
  162. typeMungers.add(new Entry(munger.getAspectType(), munger.getMunger()));
  163. }
  164. public String toString() {
  165. return "WeaverStateInfo(" + typeMungers + ", " + oldStyle + ")";
  166. }
  167. public List getTypeMungers(ResolvedType onType) {
  168. World world = onType.getWorld();
  169. List ret = new ArrayList();
  170. for (Iterator i = typeMungers.iterator(); i.hasNext();) {
  171. Entry entry = (Entry) i.next();
  172. ResolvedType aspectType = world.resolve(entry.aspectType, true);
  173. if (aspectType.isMissing()) {
  174. world.showMessage(IMessage.ERROR,
  175. WeaverMessages.format(WeaverMessages.ASPECT_NEEDED,entry.aspectType,onType),
  176. onType.getSourceLocation(), null);
  177. continue;
  178. }
  179. ret.add(new BcelTypeMunger(entry.typeMunger, aspectType));
  180. }
  181. return ret;
  182. }
  183. public boolean isOldStyle() {
  184. return oldStyle;
  185. }
  186. public byte[] getUnwovenClassFileData(byte wovenClassFile[]) {
  187. if (unwovenClassFileIsADiff){
  188. unwovenClassFile = applyDiff(wovenClassFile, unwovenClassFile);
  189. unwovenClassFileIsADiff = false;
  190. }
  191. return unwovenClassFile;
  192. }
  193. public void setUnwovenClassFileData(byte[] data) {
  194. unwovenClassFile = data;
  195. }
  196. public boolean isReweavable() {
  197. return reweavable;
  198. }
  199. public void setReweavable(boolean rw) {
  200. reweavable = rw;
  201. }
  202. public void addAspectsAffectingType(Collection /*String*/ aspects) {
  203. aspectsAffectingType.addAll(aspects);
  204. }
  205. public void addAspectAffectingType(String aspectType) {
  206. aspectsAffectingType.add(aspectType);
  207. }
  208. public Set /*String*/ getAspectsAffectingType() {
  209. return this.aspectsAffectingType;
  210. }
  211. private static void readAnyReweavableData(WeaverStateInfo wsi,DataInputStream s) throws IOException {
  212. if (wsi.isReweavable()) {
  213. // Load list of aspects that need to exist in the world for reweaving to be 'legal'
  214. int numberAspectsAffectingType = s.readShort();
  215. for (int i=0; i < numberAspectsAffectingType; i++) {wsi.addAspectAffectingType(s.readUTF());}
  216. int unwovenClassFileSize = s.readInt();
  217. byte[] classData = null;
  218. // the unwovenClassFile may have been compressed:
  219. if (wsi.reweavableCompressedMode) {
  220. classData = new byte[unwovenClassFileSize];
  221. ZipInputStream zis = new ZipInputStream(s);
  222. ZipEntry zen = zis.getNextEntry();
  223. int current = 0;
  224. int bytesToGo=unwovenClassFileSize;
  225. while (bytesToGo>0) {
  226. int amount = zis.read(classData,current,bytesToGo);
  227. current+=amount;
  228. bytesToGo-=amount;
  229. }
  230. zis.closeEntry();
  231. if (bytesToGo!=0)
  232. throw new IOException("ERROR whilst reading compressed reweavable data, expected "+
  233. unwovenClassFileSize+" bytes, only found "+current);
  234. } else {
  235. classData = new byte[unwovenClassFileSize];
  236. int bytesread = s.read(classData);
  237. if (bytesread!=unwovenClassFileSize)
  238. throw new IOException("ERROR whilst reading reweavable data, expected "+
  239. unwovenClassFileSize+" bytes, only found "+bytesread);
  240. }
  241. // if it was diffMode we'll have to remember to apply the diff if someone
  242. // asks for the unwovenClassFile
  243. wsi.unwovenClassFileIsADiff = wsi.reweavableDiffMode;
  244. wsi.setUnwovenClassFileData(classData);
  245. }
  246. }
  247. /**
  248. * Here is the cleverness for reweavable diff mode. The class file on disk contains, inside the weaverstateinfo attribute, a diff
  249. * that can be applied to 'itself' to recover the original class - which can then be rewoven.
  250. */
  251. public byte[] replaceKeyWithDiff(byte wovenClassFile[]) {
  252. // we couldn't have made the diff earlier
  253. // as we didn't have the wovenClassFile
  254. // so we left a key there as a marker to come back to
  255. if (reweavableDiffMode){
  256. ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
  257. DataOutputStream s = new DataOutputStream(arrayStream);
  258. int endOfKey = findEndOfKey(wovenClassFile);
  259. int startOfKey = endOfKey - key.length;
  260. // the length of the wsi attribute is written infront of it in the classFile,
  261. // swapping the diff for the key will probably change the length of the wsi,
  262. // so we'll have to fiddle with the four 'int length' bytes
  263. int oldLengthLocation = startOfKey -4;
  264. int oldLength = readInt(wovenClassFile, oldLengthLocation);
  265. wovenClassFile = deleteInArray(wovenClassFile,startOfKey,endOfKey); // delete the key
  266. byte [] wovenClassFileUpToWSI = new byte [oldLengthLocation];
  267. System.arraycopy(wovenClassFile,0,wovenClassFileUpToWSI,0,oldLengthLocation);
  268. byte [] diff = generateDiff(wovenClassFileUpToWSI, unwovenClassFile);
  269. try { // put the length of the diff infront of the diff
  270. s.writeInt(diff.length);
  271. s.write(diff);
  272. } catch(IOException e){}
  273. diff = arrayStream.toByteArray();
  274. // we have to swap the oldLength for the new one,
  275. // and add the diff, using the oldLength to work out where it should go :)
  276. int newLength = oldLength - key.length + diff.length;
  277. byte newLengthBytes[] = serializeInt(newLength);
  278. // swap in the serialized newLength for the oldOne:
  279. wovenClassFile[oldLengthLocation] = newLengthBytes[0];
  280. wovenClassFile[oldLengthLocation + 1] = newLengthBytes[1];
  281. wovenClassFile[oldLengthLocation + 2] = newLengthBytes[2];
  282. wovenClassFile[oldLengthLocation + 3] = newLengthBytes[3];
  283. // add the diff
  284. wovenClassFile = insertArray(diff, wovenClassFile, oldLengthLocation + 4 + oldLength - key.length);
  285. }
  286. return wovenClassFile;
  287. }
  288. private static final int findEndOfKey(byte [] wovenClassFile){
  289. // looks through the classfile backwards (as the attributes are all near the end)
  290. for(int i = wovenClassFile.length - 1; i > 0; i--)
  291. if(endOfKeyHere(wovenClassFile, i)){
  292. return i + 1;
  293. }
  294. throw new RuntimeException("key not found in wovenClassFile"); // should never happen
  295. }
  296. private static final boolean endOfKeyHere(byte lookIn[], int i){
  297. for(int j = 0; j < key.length; j++)
  298. if(key[key.length - 1 - j] != lookIn[i - j]){
  299. return false;
  300. }
  301. return true;
  302. }
  303. private static final byte[] insertArray(byte toInsert[], byte original[], int offset){
  304. byte result[] = new byte[original.length + toInsert.length];
  305. System.arraycopy(original, 0, result, 0, offset);
  306. System.arraycopy(toInsert, 0, result, offset, toInsert.length);
  307. System.arraycopy(original, offset, result, offset + toInsert.length, original.length - offset);
  308. return result;
  309. }
  310. private static final int readInt(byte [] a, int offset){
  311. ByteArrayInputStream b = new ByteArrayInputStream(a, offset, 4);
  312. DataInputStream d = new DataInputStream(b);
  313. int length = -1;
  314. try{
  315. length = d.readInt();
  316. }
  317. catch(IOException e) {
  318. throw(new RuntimeException("readInt called with a bad array or offset")); // should never happen
  319. }
  320. return length;
  321. }
  322. private static final byte[] deleteInArray(byte a[], int start, int end){
  323. int lengthToDelete = end - start;
  324. byte result[] = new byte[a.length - lengthToDelete]; // make a new array
  325. System.arraycopy(a, 0, result, 0, start); // copy in the bit before the deleted bit
  326. System.arraycopy(a, end, result, start, a.length - end); // copy in the bit after the deleted bit
  327. return result;
  328. }
  329. // ajh02: a quick note about the diff format...
  330. //
  331. // classfiles consist of:
  332. // 8 bytes: magic number and minor and major versions,
  333. // 2 bytes: its constant pool count
  334. // n bytes: the rest of the class file
  335. //
  336. // weaving a classfile never changes the classfile's first 8 bytes,
  337. // and after the constant pool count there's usually a run of bytes that weaving didn't change
  338. // hereafter referred to as the run
  339. //
  340. // so the diff consists of:
  341. // 2 bytes: its constant pool count
  342. // 4 bytes: length of the run
  343. // n bytes: the rest of the unwovenClassFile
  344. byte [] generateDiff(byte [] wovenClassFile, byte [] unWovenClassFile){
  345. // find how long the run is
  346. int lookingAt = 10;
  347. int shorterLength
  348. =(wovenClassFile.length < unWovenClassFile.length)? wovenClassFile.length:unWovenClassFile.length;
  349. while (lookingAt < shorterLength && (wovenClassFile[lookingAt] == unWovenClassFile[lookingAt])){
  350. lookingAt++;
  351. }
  352. int lengthInCommon = lookingAt - 10;
  353. byte [] diff = new byte [unWovenClassFile.length - 4 - lengthInCommon];
  354. // first 2 bytes of the diff are the constant pool count
  355. diff[0] = unWovenClassFile[8];
  356. diff[1] = unWovenClassFile[9];
  357. // then 4 bytes saying how long the run is
  358. byte [] lengthInCommonBytes = serializeInt(lengthInCommon);
  359. diff[2] = lengthInCommonBytes[0];
  360. diff[3] = lengthInCommonBytes[1];
  361. diff[4] = lengthInCommonBytes[2];
  362. diff[5] = lengthInCommonBytes[3];
  363. // then we just dump the rest of the unWovenClassFile verbatim
  364. System.arraycopy(unWovenClassFile,10+lengthInCommon,diff,6,diff.length-6);
  365. return diff;
  366. }
  367. byte [] applyDiff(byte [] wovenClassFile, byte [] diff){
  368. int lengthInCommon = readInt(diff,2);
  369. byte [] unWovenClassFile = new byte [4 + diff.length + lengthInCommon];
  370. // copy the first 8 bytes from the wovenClassFile
  371. System.arraycopy(wovenClassFile,0,unWovenClassFile,0,8);
  372. // copy the constant pool count from the diff
  373. unWovenClassFile[8] = diff[0];
  374. unWovenClassFile[9] = diff[1];
  375. // copy the run from the wovenClassFile
  376. System.arraycopy(wovenClassFile,10,unWovenClassFile,10,lengthInCommon);
  377. // copy the stuff after the run from the diff
  378. System.arraycopy(diff,6,unWovenClassFile,10+lengthInCommon,diff.length-6);
  379. return unWovenClassFile;
  380. }
  381. private byte [] serializeInt(int i){
  382. ByteArrayOutputStream bos = new ByteArrayOutputStream(4);
  383. DataOutputStream dos = new DataOutputStream(bos);
  384. try {
  385. dos.writeInt(i);
  386. } catch(IOException e) {}
  387. return bos.toByteArray();
  388. }
  389. private static void writeAnyReweavableData(WeaverStateInfo wsi,DataOutputStream s) throws IOException {
  390. if (wsi.isReweavable()) {
  391. // Write out list of aspects that must exist next time we try and weave this class
  392. s.writeShort(wsi.aspectsAffectingType.size());
  393. if (wsi.aspectsAffectingType.size()>0) {
  394. for (Iterator iter = wsi.aspectsAffectingType.iterator(); iter.hasNext();) {
  395. String type = (String) iter.next();
  396. s.writeUTF(type);
  397. }
  398. }
  399. byte[] data = wsi.unwovenClassFile;
  400. // if we're not in diffMode, write the unwovenClassFile now,
  401. // otherwise we'll insert it as a diff later
  402. if (!wsi.reweavableDiffMode) {
  403. s.writeInt(data.length);
  404. s.write(wsi.unwovenClassFile);
  405. }
  406. }
  407. }
  408. }