Index: src/org/aspectj/weaver/WeaverStateInfo.java =================================================================== RCS file: /home/technology/org.aspectj/modules/weaver/src/org/aspectj/weaver/WeaverStateInfo.java,v retrieving revision 1.6 diff -u -r1.6 WeaverStateInfo.java --- src/org/aspectj/weaver/WeaverStateInfo.java 15 Jul 2005 16:09:03 -0000 1.6 +++ src/org/aspectj/weaver/WeaverStateInfo.java 21 Sep 2005 14:56:15 -0000 @@ -30,6 +30,8 @@ import org.aspectj.bridge.IMessage; import org.aspectj.weaver.bcel.BcelTypeMunger; +import java.io.ByteArrayInputStream; // ajh02: added +import java.io.ByteArrayOutputStream; // ajh02: added /** * WeaverStateInfo represents how a type was processed. It is used by the weaver to determine how a type @@ -60,6 +62,13 @@ private static boolean reweavableDefault = false; private static boolean reweavableCompressedModeDefault = false; + private static byte key[] = { // ajh02: added + // could be longer if it's not going in the file written to disk + // (which I'm pretty sure it doesn't have to) + -51, 34, 105, 56, -34, 65, 45, 78 + }; + private boolean unwovenClassFileDataIsDiff; // ajh02: added + public WeaverStateInfo() { this(new ArrayList(), false,reweavableDefault,reweavableCompressedModeDefault); } @@ -71,6 +80,7 @@ this.reweavableCompressedMode = reweavableCompressedMode; this.aspectsAffectingType= new HashSet(); this.unwovenClassFile = null; + unwovenClassFileDataIsDiff = false; // ajh02: line added } public static void setReweavableModeDefaults(boolean mode, boolean compress) { @@ -85,6 +95,7 @@ private static final byte REWEAVABLE_COMPRESSION_BIT = 1<<5; public static final WeaverStateInfo read(VersionedDataInputStream s, ISourceContext context) throws IOException { + System.err.println("read called"); byte b = s.readByte(); boolean isReweavable = ((b&REWEAVABLE_BIT)!=0); @@ -130,8 +141,13 @@ } public void write(DataOutputStream s) throws IOException { + System.err.println("write called"); if (oldStyle) throw new RuntimeException("shouldn't be writing this"); + if (reweavable && reweavableCompressedMode){ + s.write(key); // ajh02: 3 lines added + } + byte weaverStateInfoKind = EXTENDED; if (reweavable) weaverStateInfoKind |= REWEAVABLE_BIT; if (reweavableCompressedMode) weaverStateInfoKind |= REWEAVABLE_COMPRESSION_BIT; @@ -176,10 +192,40 @@ public boolean isOldStyle() { return oldStyle; } - - public byte[] getUnwovenClassFileData() { - return unwovenClassFile; - } + + public byte[] getUnwovenClassFileData(byte wovenClassFile[]) { + // ajh02: method added + System.err.println("getUnwovenClassFileData called"); + if(!unwovenClassFileDataIsDiff){ + return unwovenClassFile; + } else { + + // to apply the diff we use a version of wovenClassFile + // up to but not including the length of the WeaverStateInfo... + // BUT actually it shouldn't matter if we give it a bit too much! + // so we can actually just give it the whole wovenClassFile here!!!!! :D + // so we don't need to do any horrible fiddling or serializing keys + // to find where the length of the weaverStateInfo was serialized :) + // DUDE! sweet! + + unwovenClassFile = applyDiff(wovenClassFile, unwovenClassFile); + unwovenClassFileDataIsDiff = false; + +// int endOfKey = findEndOfKey(wovenClassFile); +// int positionOfnewLength = endOfKey - key.length - 9; +// int newLength = readInt(wovenClassFile, positionOfnewLength); +// int oldLength = readInt(wovenClassFile, positionOfnewLength + 5); +// byte oldLengthAndB[] = { +// wovenClassFile[positionOfnewLength + 5], wovenClassFile[positionOfnewLength + 6], wovenClassFile[positionOfnewLength + 7], wovenClassFile[positionOfnewLength + 8], wovenClassFile[positionOfnewLength + 4] +// }; +// byte withoutZippedDiff[] = deleteInArray(wovenClassFile, positionOfnewLength + 8 + oldLength, positionOfnewLength + 4 + newLength); +// withoutZippedDiff = deleteInArray(withoutZippedDiff, positionOfnewLength, positionOfnewLength + 9); +// withoutZippedDiff = insertArray(oldLengthAndB, withoutZippedDiff, positionOfnewLength); +// unwovenClassFile = applyDiff(withoutZippedDiff, unwovenClassFile); +// unwovenClassFileDataIsDiff = false; + return unwovenClassFile; + } + } public void setUnwovenClassFileData(byte[] data) { unwovenClassFile = data; @@ -205,10 +251,10 @@ } - //// + // ajh02: a load of methods in this class should be made non-static on their wsi's, shouldn't they + // because it's just silly making it static then always giving it a wsi that can't be null anyway private static void readAnyReweavableData(WeaverStateInfo wsi,DataInputStream s) throws IOException { - if (wsi.isReweavable()) { // Load list of aspects that need to exist in the world for reweaving to be 'legal' int numberAspectsAffectingType = s.readShort(); @@ -224,31 +270,264 @@ if (bytesread!=unwovenClassFileSize) throw new IOException("ERROR whilst reading reweavable data, expected "+ unwovenClassFileSize+" bytes, only found "+bytesread); + wsi.unwovenClassFileDataIsDiff = false; } else { // Decompress it +// classData = new byte[unwovenClassFileSize]; +// +// ZipInputStream zis = new ZipInputStream(s); +// ZipEntry zen = zis.getNextEntry(); +// int current = 0; +// int bytesToGo=unwovenClassFileSize; +// while (bytesToGo>0) { +// int amount = zis.read(classData,current,bytesToGo); +// current+=amount; +// bytesToGo-=amount; +// } +// zis.closeEntry(); +// if (bytesToGo!=0) +// throw new IOException("ERROR whilst reading compressed reweavable data, expected "+ +// unwovenClassFileSize+" bytes, only found "+current); + classData = new byte[unwovenClassFileSize]; - - ZipInputStream zis = new ZipInputStream(s); - ZipEntry zen = zis.getNextEntry(); - int current = 0; - int bytesToGo=unwovenClassFileSize; - while (bytesToGo>0) { - int amount = zis.read(classData,current,bytesToGo); - current+=amount; - bytesToGo-=amount; - } - zis.closeEntry(); - if (bytesToGo!=0) - throw new IOException("ERROR whilst reading compressed reweavable data, expected "+ - unwovenClassFileSize+" bytes, only found "+current); + int bytesread = s.read(classData); + if (bytesread!=unwovenClassFileSize) + throw new IOException("ERROR whilst reading reweavable data, expected "+ + unwovenClassFileSize+" bytes, only found "+bytesread); + // but remember it's a diff of the unwoven classFile! + wsi.unwovenClassFileDataIsDiff = true; } wsi.setUnwovenClassFileData(classData); } } + public byte[] insertAnyReweavableData(byte wovenClassFile[]) { + // now that we have the whole wovenClassFile, + //we can make the diff with it and write it over where we previously left the + // key stub + System.err.println("insertAnyReweavableData called"); + //return wovenClassFile; + + if (isReweavable() && reweavableCompressedMode){ + ByteArrayOutputStream arrayStream = new ByteArrayOutputStream(); + DataOutputStream s = new DataOutputStream(arrayStream); + + int endOfKey = findEndOfKey(wovenClassFile); + int startOfKey = endOfKey - key.length; + int oldLengthLocation = startOfKey -4; + int oldLength = readInt(wovenClassFile, oldLengthLocation); + wovenClassFile = deleteInArray(wovenClassFile,startOfKey,endOfKey); // remove the key + + byte [] wovenClassFileToDiffWith = new byte [oldLengthLocation]; + System.arraycopy(wovenClassFile,0,wovenClassFileToDiffWith,0,oldLengthLocation); + + // to make the diff we use a version of wovenClassFile + // up to but not including the length of the WeaverStateInfo + + byte [] diff = generateDiff(wovenClassFileToDiffWith, unwovenClassFile); + try { // add the length of the diff to the front of the diff + s.writeInt(diff.length); + s.write(diff); + } catch(IOException e){} + diff = arrayStream.toByteArray(); + // we have to swap the oldLength for the new one, + // and add the diff (can use the oldLength to work out where it goes :) + + int newLength = oldLength - key.length + diff.length; + byte newLengthBytes[] = serializeInt(newLength); + + // swap in the serialized newLength for the oldOne: + wovenClassFile[oldLengthLocation] = newLengthBytes[0]; + wovenClassFile[oldLengthLocation + 1] = newLengthBytes[1]; + wovenClassFile[oldLengthLocation + 2] = newLengthBytes[2]; + wovenClassFile[oldLengthLocation + 3] = newLengthBytes[3]; + + // add the diff + wovenClassFile = insertArray(diff, wovenClassFile, oldLengthLocation + 4 + oldLength - key.length); + + + } + return wovenClassFile; + + // ajh02: method added +// ByteArrayOutputStream arrayStream = new ByteArrayOutputStream(); +// DataOutputStream s = new DataOutputStream(arrayStream); +// if(isReweavable()){ +// try { +// if(!reweavableCompressedMode) { +// s.writeInt(unwovenClassFile.length); +// s.write(unwovenClassFile); +// } else { +// byte diff[] = generateDiff(wovenClassFile, unwovenClassFile); +// s.writeInt(diff.length); +// s.write(diff); +// } +// } +// catch(IOException e) { +// // ajh02: hmm do something here? +// // can this ever happen? +// } +// byte zippedDiff[] = arrayStream.toByteArray(); +// int endOfKey = findEndOfKey(wovenClassFile); +// int oldLengthLocation = endOfKey - key.length - 5; +// int oldLength = readInt(wovenClassFile, oldLengthLocation); +// int newLength = oldLength + 4 + zippedDiff.length; +// ByteArrayOutputStream bos = new ByteArrayOutputStream(4); +// DataOutputStream dos = new DataOutputStream(bos); +// try { +// dos.writeInt(newLength); +// } +// catch(IOException e) { +// } +// byte newLengthBytes[] = bos.toByteArray(); +// byte oldLengthBytes[] = { +// wovenClassFile[oldLengthLocation], wovenClassFile[oldLengthLocation + 1], wovenClassFile[oldLengthLocation + 2], wovenClassFile[oldLengthLocation + 3] +// }; +// wovenClassFile[oldLengthLocation] = newLengthBytes[0]; +// wovenClassFile[oldLengthLocation + 1] = newLengthBytes[1]; +// wovenClassFile[oldLengthLocation + 2] = newLengthBytes[2]; +// wovenClassFile[oldLengthLocation + 3] = newLengthBytes[3]; +// wovenClassFile = insertArray(oldLengthBytes, wovenClassFile, oldLengthLocation + 5); +// return insertArray(zippedDiff, wovenClassFile, oldLengthLocation + 8 + oldLength); +// } else { +// return wovenClassFile; +// } + } + + private static final int findEndOfKey(byte lookIn[]){ + // looks through the classfile backwards (as the attributes are all near the end) + for(int i = lookIn.length - 1; i > 0; i--) + if(endOfKeyHere(lookIn, i)){ + return i + 1; + } + throw new RuntimeException("key not found in wovenClassFile"); + } + private static final boolean endOfKeyHere(byte lookIn[], int i){ + for(int j = 0; j < key.length; j++) + if(key[key.length - 1 - j] != lookIn[i - j]){ + return false; + } + return true; + } + private static final byte[] insertArray(byte toInsert[], byte original[], int offset){ + byte result[] = new byte[original.length + toInsert.length]; + System.arraycopy(original, 0, result, 0, offset); + System.arraycopy(toInsert, 0, result, offset, toInsert.length); + System.arraycopy(original, offset, result, offset + toInsert.length, original.length - offset); + return result; + } + private static final int readInt(byte [] a, int offset){ + ByteArrayInputStream b = new ByteArrayInputStream(a, offset, 4); + DataInputStream d = new DataInputStream(b); + int length = -1; + try{ + length = d.readInt(); + } + catch(IOException e) { + // ajh02: can this ever happen? + } + return length; + } + private static final int readUnsignedShort(byte a[], int offset){ + ByteArrayInputStream b = new ByteArrayInputStream(a, offset, 4); + DataInputStream d = new DataInputStream(b); + int length = -1; + try{ + length = d.readUnsignedShort(); + } + catch(IOException e) { + // ajh02: can this ever happen? + } + return length; + } + private static final byte[] deleteInArray(byte a[], int start, int end){ + int lengthToDelete = end - start; + byte result[] = new byte[a.length - lengthToDelete]; // make a new array + System.arraycopy(a, 0, result, 0, start); // copy in the bit before the deleted bit + System.arraycopy(a, end, result, start, a.length - end); // copy in the bit after the deleted bit + return result; + } + + // classfiles consist of: + // 8 bytes: magic number and minor and major versions, + // 2 bytes: its constant pool count + // n bytes: the rest of the class file + // + // weaving a classfile never changes the classfile's first 8 bytes, + // and usually there's a load of bytes 10 bytes in that weaving leaves unchanged + // + // so the diff consists of: + // 2 bytes: its constant pool count + // 4 bytes: the number of bytes the woven and unWoven class files have in common 10 bytes in + // n bytes: the unWoven class file after the bit it has in common with the woven one + + byte [] applyDiff(byte [] wovenClassFile, byte [] diff){ + + int lengthInCommon = readInt(diff,2); + byte [] unWovenClassFile = new byte [4 + diff.length + lengthInCommon]; + + // the magic number and classfile version cannot be changed by weaving + System.arraycopy(wovenClassFile,0,unWovenClassFile,0,8); + + // copy across the constant pool count + unWovenClassFile[8] = diff[0]; + unWovenClassFile[9] = diff[1]; + + // copy in the stuff they have in common + System.arraycopy(wovenClassFile,10,unWovenClassFile,10,lengthInCommon); + + // now copy the rest of the diff in verbatim + System.arraycopy(diff,6,unWovenClassFile,10+lengthInCommon,diff.length-6); + + return unWovenClassFile; + } + + byte [] generateDiff(byte [] wovenClassFile, byte [] unWovenClassFile){ + + // find how long the bit 10 bytes in that they have in common is + int lookingAt = 10; + int shorterLength + =(wovenClassFile.length < unWovenClassFile.length)? wovenClassFile.length:unWovenClassFile.length; + while (lookingAt < shorterLength && (wovenClassFile[lookingAt] == unWovenClassFile[lookingAt])){ + lookingAt++; + } + int lengthInCommon = lookingAt - 10; + byte [] diff = new byte [unWovenClassFile.length - 4 - lengthInCommon]; + + // first 2 bytes of the diff are the constant pool count + diff[0] = unWovenClassFile[8]; + diff[1] = unWovenClassFile[9]; + + // then 4 bytes saying how long they have in common after the constant pool count + byte [] lengthInCommonBytes = serializeInt(lengthInCommon); + diff[2] = lengthInCommonBytes[0]; + diff[3] = lengthInCommonBytes[1]; + diff[4] = lengthInCommonBytes[2]; + diff[5] = lengthInCommonBytes[3]; + + // then we just dump the rest of the unWovenClassFile verbatim + System.arraycopy(unWovenClassFile,10+lengthInCommon,diff,6,diff.length-6); + + System.err.println("unwoven class file was: " + unWovenClassFile.length); + System.err.println("diff is: " + diff.length); + + return diff; + } + + private byte [] serializeInt(int i){ + ByteArrayOutputStream bos = new ByteArrayOutputStream(4); + DataOutputStream dos = new DataOutputStream(bos); + try { + dos.writeInt(i); + } catch(IOException e) {} + return bos.toByteArray(); + } + + private static void writeAnyReweavableData(WeaverStateInfo wsi,DataOutputStream s) throws IOException { + System.err.println("writeAnyReweavableData called"); if (wsi.isReweavable()) { // Write out list of aspects that must exist next time we try and weave this class s.writeShort(wsi.aspectsAffectingType.size()); @@ -259,16 +538,23 @@ } } byte[] data = wsi.unwovenClassFile; - s.writeInt(data.length); + // Do we need to compress the data? if (!wsi.reweavableCompressedMode) { + s.writeInt(data.length); s.write(wsi.unwovenClassFile); } else { - ZipOutputStream zos = new ZipOutputStream(s); - ZipEntry ze = new ZipEntry("data"); - zos.putNextEntry(ze); - zos.write(wsi.unwovenClassFile,0,wsi.unwovenClassFile.length); - zos.closeEntry(); + + //s.write(key); // ajh02: line added + // put the key here as a marker for where we have to come back and + // fill in later + + + //ZipOutputStream zos = new ZipOutputStream(s); + //ZipEntry ze = new ZipEntry("data"); + //zos.putNextEntry(ze); + //zos.write(wsi.unwovenClassFile,0,wsi.unwovenClassFile.length); + //zos.closeEntry(); } } } Index: src/org/aspectj/weaver/bcel/BcelClassWeaver.java =================================================================== RCS file: /home/technology/org.aspectj/modules/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java,v retrieving revision 1.51 diff -u -r1.51 BcelClassWeaver.java --- src/org/aspectj/weaver/bcel/BcelClassWeaver.java 1 Sep 2005 12:35:40 -0000 1.51 +++ src/org/aspectj/weaver/bcel/BcelClassWeaver.java 21 Sep 2005 14:56:16 -0000 @@ -1603,7 +1603,7 @@ private void matchGetInstruction(LazyMethodGen mg, InstructionHandle ih, BcelShadow enclosingShadow, List shadowAccumulator) { FieldInstruction fi = (FieldInstruction) ih.getInstruction(); Member field = BcelWorld.makeFieldJoinPointSignature(clazz, fi); - + // synthetic fields are never join points if (field.getName().startsWith(NameMangler.PREFIX)) return; @@ -1615,7 +1615,12 @@ // sets of synthetics aren't join points in 1.1 return; } else { - match(BcelShadow.makeFieldGet(world, resolvedField, mg, ih, enclosingShadow), shadowAccumulator); + BcelShadow bs = BcelShadow.makeFieldGet(world,resolvedField,mg,ih,enclosingShadow); + String cname = fi.getClassName(cpg); + if (!resolvedField.getDeclaringType().getName().equals(cname)) { + bs.setActualTargetType(cname); + } + match(bs, shadowAccumulator); } } Index: src/org/aspectj/weaver/bcel/BcelShadow.java =================================================================== RCS file: /home/technology/org.aspectj/modules/weaver/src/org/aspectj/weaver/bcel/BcelShadow.java,v retrieving revision 1.73 diff -u -r1.73 BcelShadow.java --- src/org/aspectj/weaver/bcel/BcelShadow.java 12 Sep 2005 20:17:19 -0000 1.73 +++ src/org/aspectj/weaver/bcel/BcelShadow.java 21 Sep 2005 14:56:17 -0000 @@ -133,6 +133,10 @@ private final BcelWorld world; private final LazyMethodGen enclosingMethod; private boolean fallsThrough; //XXX not used anymore + + // Some instructions have a target type that will vary + // from the signature (pr109728) (1.4 declaring type issue) + private String actualInstructionTargetType; // ---- initialization @@ -1221,6 +1225,8 @@ * are true, it has a sneak peek at the code before the call to see what is on the stack. */ public UnresolvedType ensureTargetTypeIsCorrect(UnresolvedType tx) { + + if (tx.equals(ResolvedType.OBJECT) && getKind() == MethodCall && getSignature().getReturnType().equals(ResolvedType.OBJECT) && getSignature().getArity()==0 && @@ -2938,6 +2944,15 @@ if (targetVar != null && targetVar != thisVar) { UnresolvedType targetType = getTargetType(); targetType = ensureTargetTypeIsCorrect(targetType); + // see pr109728 - this fixes the case when the declaring class is sometype 'X' but the getfield + // in the bytecode refers to a subtype of 'X'. This makes sure we use the type originally + // mentioned in the fieldget instruction as the method parameter and *not* the type upon which the + // field is declared because when the instructions are extracted into the new around body, + // they will still refer to the subtype. + if (getKind()==FieldGet && getActualTargetType()!=null && + !getActualTargetType().equals(targetType.getName())) { + targetType = UnresolvedType.forName(getActualTargetType()).resolve(world); + } ResolvedMember resolvedMember = getSignature().resolve(world); if (resolvedMember != null && Modifier.isProtected(resolvedMember.getModifiers()) && @@ -3072,4 +3087,12 @@ public boolean isFallsThrough() { return !terminatesWithReturn(); //fallsThrough; } + + public void setActualTargetType(String className) { + this.actualInstructionTargetType = className; + } + + public String getActualTargetType() { + return actualInstructionTargetType; + } } Index: src/org/aspectj/weaver/bcel/BcelWeaver.java =================================================================== RCS file: /home/technology/org.aspectj/modules/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java,v retrieving revision 1.62 diff -u -r1.62 BcelWeaver.java --- src/org/aspectj/weaver/bcel/BcelWeaver.java 2 Sep 2005 10:40:19 -0000 1.62 +++ src/org/aspectj/weaver/bcel/BcelWeaver.java 21 Sep 2005 14:56:17 -0000 @@ -1146,7 +1146,8 @@ } } } - classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass().getFileName(), wsi.getUnwovenClassFileData())); + //classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass().getFileName(), wsi.getUnwovenClassFileData())); // ajh02: line changed + classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass().getFileName(), wsi.getUnwovenClassFileData(classType.getJavaClass().getBytes()))); } else { classType.resetState(); } @@ -1184,7 +1185,7 @@ public UnwovenClassFile[] getClassFilesFor(LazyClassGen clazz) { List childClasses = clazz.getChildClasses(world); UnwovenClassFile[] ret = new UnwovenClassFile[1 + childClasses.size()]; - ret[0] = new UnwovenClassFile(clazz.getFileName(),clazz.getJavaClass(world).getBytes()); + ret[0] = new UnwovenClassFile(clazz.getFileName(),clazz.getJavaClassBytesIncludingReweavable(world)); int index = 1; for (Iterator iter = childClasses.iterator(); iter.hasNext();) { UnwovenClassFile.ChildClass element = (UnwovenClassFile.ChildClass) iter.next(); Index: src/org/aspectj/weaver/bcel/LazyClassGen.java =================================================================== RCS file: /home/technology/org.aspectj/modules/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java,v retrieving revision 1.40 diff -u -r1.40 LazyClassGen.java --- src/org/aspectj/weaver/bcel/LazyClassGen.java 12 Sep 2005 20:17:19 -0000 1.40 +++ src/org/aspectj/weaver/bcel/LazyClassGen.java 21 Sep 2005 14:56:17 -0000 @@ -600,6 +600,20 @@ writeBack(world); return myGen.getJavaClass(); } + + private boolean reweavableDataInserted; // ajh02: added + public byte[] getJavaClassBytesIncludingReweavable(BcelWorld world){ + // ajh02: method added + writeBack(world); + byte wovenClassFileData[] = myGen.getJavaClass().getBytes(); + WeaverStateInfo wsi = getOrCreateWeaverStateInfo(); + if(wsi != null && !reweavableDataInserted){ + reweavableDataInserted = true; + return wsi.insertAnyReweavableData(wovenClassFileData); + } else{ + return wovenClassFileData; + } + } public void addGeneratedInner(LazyClassGen newClass) { classGens.add(newClass);