Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import java.io.IOException;
import java.nio.ByteBuffer;


public class AnnotationConstantPool {
private static final String CLASSPORT_DESCRIPTOR =
String.format("L%s;", ClassportInfo.class.getName().replace('.', '/'));

private ClassportInfo annotationInfo;

public AnnotationConstantPool(ClassportInfo annotationInfo) {
Expand All @@ -21,7 +23,7 @@ public ConstantPoolData getNewEntries() {
try {
int entryCount = 0;
// Annotation type descriptor
writeUtf8Entry(dos, String.format("L%s;", ClassportInfo.class.getName().replace('.', '/')));
writeUtf8Entry(dos, CLASSPORT_DESCRIPTOR);
entryCount++;

// Group (key)
Expand Down Expand Up @@ -104,19 +106,10 @@ public byte[] injectAnnotation(byte[] originalBytes, ConstantPoolData cpd) {
byte[] existingCpData = new byte[cpEnd - cpStart];
originalBuffer.position(cpStart);
originalBuffer.get(existingCpData);

int rvaUtf8Index = findUtf8InConstantPool(existingCpData, originalConstantPoolCount - 1, "RuntimeVisibleAnnotations");
boolean rvaExists = rvaUtf8Index > 0;

// check if ClassportInfo is already in the constant pool
int classportInfoIndex = findUtf8InConstantPool(existingCpData, originalConstantPoolCount - 1, String.format("L%s;", ClassportInfo.class.getName().replace('.', '/')));
boolean classportInfoExists = classportInfoIndex > 0;
if (classportInfoExists) {
// we don't want duplicate classport info entries
// so we return the original bytes
return originalBytes;
}

int newConstantPoolCount = originalConstantPoolCount + cpd.entryCount();
if (!rvaExists) {
newConstantPoolCount++; // add one for "RuntimeVisibleAnnotations"
Expand Down Expand Up @@ -160,7 +153,7 @@ public byte[] injectAnnotation(byte[] originalBytes, ConstantPoolData cpd) {
byte[] attrData = new byte[attrLen];
originalBuffer.get(attrData);

if (i == rvaIndex) {
if (i == rvaIndex && !hasClassportAnnotation(originalBytes, attrData)) {
// Merge annotation
dos.writeShort(nameIndex);
byte[] merged = mergeAnnotation(attrData, indices);
Expand Down Expand Up @@ -282,6 +275,61 @@ private void writeStringElementValue(DataOutputStream out, int nameIndex, int va
out.writeShort(valueIndex);
}

private boolean hasClassportAnnotation(byte[] classBytes, byte[] rvaAttrData) {
ByteBuffer buffer = ByteBuffer.wrap(rvaAttrData);
int annotationCount = buffer.getShort() & 0xFFFF;

for (int i = 0; i < annotationCount; i++) {
int typeIndex = buffer.getShort() & 0xFFFF;
String descriptor = getCPString(classBytes, typeIndex);
if (CLASSPORT_DESCRIPTOR.equals(descriptor)) {
return true;
}
skipAnnotation(buffer);
}
return false;
}

private void skipAnnotation(ByteBuffer buffer) {
if (buffer.remaining() < 2) return;
int numPairs = buffer.getShort() & 0xFFFF;
for (int j = 0; j < numPairs; j++) {
if (buffer.remaining() < 2) return;
buffer.getShort(); // skip element_name_index
skipElementValue(buffer); // skip element_value
}
}

private void skipElementValue(ByteBuffer buffer) {
if (buffer.remaining() < 1) return;
byte tag = buffer.get();
switch (tag) {
case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': case 's': // primitive or string
if (buffer.remaining() < 2) return;
buffer.getShort(); // const_value_index
break;
case 'e': // enum
if (buffer.remaining() < 4) return;
buffer.getShort(); // type_name_index
buffer.getShort(); // const_name_index
break;
case 'c': // class
if (buffer.remaining() < 2) return;
buffer.getShort(); // class_info_index
break;
case '@': // annotation (recursive)
skipAnnotation(buffer);
break;
case '[': // array
if (buffer.remaining() < 2) return;
int numValues = buffer.getShort() & 0xFFFF;
for (int k = 0; k < numValues; k++) {
skipElementValue(buffer);
}
break;
}
}

private int findRuntimeVisibleAnnotationsAttribute(byte[] classBytes, int attrStartPos, int attrCount) {
ByteBuffer buffer = ByteBuffer.wrap(classBytes);
buffer.position(attrStartPos);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,28 @@ void injectAnnotation_existingAnnotations() throws IOException {
// assert
assertArrayEquals(resultBytes, expectedBytes);
}

@Test
void injectAnnotation_internalClassportClass() throws IOException {
// arrange
ClassportInfo annotationInfo = new ClassportHelper().getInstance(
"foo",
true,
"bar:jar:1.0.0",
"bar",
"foo",
"1.0.0",
new String[] {}
);
byte[] originalBytes = Files.readAllBytes(TEST_RESOURCES_DIR.resolve("ClassportInternal_original.class"));
byte[] expectedBytes = Files.readAllBytes(TEST_RESOURCES_DIR.resolve("ClassportInternal_annotated.class"));

// act
AnnotationConstantPool annotationConstantPool = new AnnotationConstantPool(annotationInfo);
byte[] resultBytes = annotationConstantPool.injectAnnotation(originalBytes, annotationConstantPool.getNewEntries());

// assert
assertArrayEquals(resultBytes, expectedBytes);

}
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ private void embedDirectory(Artifact a, File dirToWalk) throws IOException, Mojo
Files.walkFileTree(dirToWalk.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
getLog().debug("Processing file: " + file.getFileName());
try (FileInputStream in = new FileInputStream(file.toFile())) {
byte[] bytes = in.readAllBytes();

Expand Down