Skip to content

Commit

Permalink
Merge pull request #299 from pietrobraione/master
Browse files Browse the repository at this point in the history
Fix renaming of classes in presence of generic signatures and nested classes.
This pull request has some problems but I'll fix them later.
  • Loading branch information
chibash committed May 11, 2022
2 parents 01c788f + da39aa8 commit c8b54b3
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 25 deletions.
94 changes: 69 additions & 25 deletions src/main/javassist/bytecode/SignatureAttribute.java
Expand Up @@ -19,6 +19,7 @@
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -110,41 +111,84 @@ static String renameClass(String desc, String oldname, String newname) {
}

static String renameClass(String desc, Map<String,String> map) {
if (map == null)
if (map == null || map.isEmpty())
return desc;

StringBuilder newdesc = new StringBuilder();
int head = 0;
int i = 0;
for (;;) {
int j = desc.indexOf('L', i);
final int j = desc.indexOf('L', i);
if (j < 0)
break;

int k = desc.indexOf(';', j);
if (k < 0)
break;

int l = desc.indexOf('<', j);
int classEndIndex;
char classEndChar;
if (l < 0 || k < l) {
classEndIndex = k;
classEndChar = ';';
} else {
classEndIndex = l;
classEndChar = '<';
StringBuilder nameBuf = new StringBuilder();
StringBuilder genericParamBuf = new StringBuilder();
final ArrayList<StringBuilder> nameBufs = new ArrayList<>();
final ArrayList<StringBuilder> genericParamBufs = new ArrayList<>();
int k = j;
char c;
try {
while ((c = desc.charAt(++k)) != ';') {
if (c == '<') {
genericParamBuf.append(c);
int level = 1;
while (level > 0) {
c = desc.charAt(++k);
genericParamBuf.append(c);
if (c == '<') ++level;
else if (c == '>') --level;
}
} else if (c == '.') {
nameBufs.add(nameBuf);
genericParamBufs.add(genericParamBuf);
nameBuf = new StringBuilder();
genericParamBuf = new StringBuilder();
} else {
nameBuf.append(c);
}
}
}
i = classEndIndex + 1;

String name = desc.substring(j + 1, classEndIndex);
String name2 = map.get(name);
if (name2 != null) {
newdesc.append(desc.substring(head, j));
newdesc.append('L');
newdesc.append(name2);
newdesc.append(classEndChar);
head = i;
catch (IndexOutOfBoundsException e) { break; }

nameBufs.add(nameBuf);
genericParamBufs.add(genericParamBuf);
i = k + 1;

String name = String.join("$", nameBufs.toArray(new StringBuilder[0]));
String newname = map.get(name);
if (newname != null) {
final String[] nameSplit = name.split("\\$");
final String[] newnameSplit = newname.split("\\$");
if (nameSplit.length == newnameSplit.length) {
final String[] newnames = new String[nameBufs.size()];
for (int start = 0, z = 0; z < nameBufs.size(); ++z) {
final int toAggregate = (int) nameBufs.get(z).chars().filter(ch -> ch == '$').count() + 1;
String s = String.join("$", Arrays.copyOfRange(newnameSplit, start, start + toAggregate));
start += toAggregate;
newnames[z] = s;
}


newdesc.append(desc.substring(head, j));
newdesc.append('L');
for (int z = 0; z < newnames.length; ++z) {
if (z > 0) {
newdesc.append('.');
}
newdesc.append(newnames[z]);
final String newgenericParam;
final StringBuilder genericParamBufCurrent = genericParamBufs.get(z);
if (genericParamBufCurrent.length() > 0) {
newgenericParam = "<" + renameClass(genericParamBufCurrent.substring(1, genericParamBufCurrent.length() - 1), map) + ">";
} else {
newgenericParam = genericParamBufCurrent.toString(); //empty string
}
newdesc.append(newgenericParam);
}
newdesc.append(c); //the final semicolon
head = i;
}
}
}

Expand Down
84 changes: 84 additions & 0 deletions src/test/javassist/bytecode/SignatureAttributeTest.java
@@ -0,0 +1,84 @@
package javassist.bytecode;

import static org.junit.Assert.assertEquals;

import java.util.HashMap;

public class SignatureAttributeTest {
void test1() {
final String signature = "TX;TY;La/b/C$D$E$J$K;"; //a sequence of three ReferenceTypeSignature
final HashMap<String, String> map = new HashMap<>();
map.put("a/b/C$D$E$J$K", "o/p/Q$R$S$T$U");
map.put("e/F$G$H$I", "v/W$X$Y$Z");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("TX;TY;Lo/p/Q$R$S$T$U;", signatureRenamed);
}

void test2() {
final String signature = "La/b/C<TA;TB;>.D<Ljava/lang/Integer;>;"; //a ClassTypeSignature
final HashMap<String, String> map = new HashMap<>();
map.put("a/b/C$D", "o/p/Q$R");
map.put("java/lang/Integer", "java/lang/Long");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("Lo/p/Q<TA;TB;>.R<Ljava/lang/Long;>;", signatureRenamed);
}

void test3() {
final String signature = "BJLB<TX;Lc/D$E;>.F<TY;>;TZ;"; //a sequence of four JavaTypeSignature
final HashMap<String, String> map = new HashMap<>();
map.put("B$F", "P$T");
map.put("c/D$E", "q/R$S");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("BJLP<TX;Lq/R$S;>.T<TY;>;TZ;", signatureRenamed);
}

void test4() {
final String signature = "La/b/C<TX;>;[[Ld/E<+TY;-Ljava/lang/Object;*>;Z"; //a sequence of three JavaTypeSignature
final HashMap<String, String> map = new HashMap<>();
map.put("java/lang/Object", "java/util/Map");
map.put("d/E", "F");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("La/b/C<TX;>;[[LF<+TY;-Ljava/util/Map;*>;Z", signatureRenamed);
}

void test5() {
final String signature = "La/b/C$D$E<TX;Le/F$G<TY;TZ;>.H$I<TU;TV;>;>.J$K;"; //a ClassTypeSignature
final HashMap<String, String> map = new HashMap<>();
map.put("a/b/C$D$E$J$K", "o/p/Q$R$S$T$U");
map.put("e/F$G$H$I", "v/W$X$Y$Z");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("Lo/p/Q$R$S<TX;Lv/W$X<TY;TZ;>.Y$Z<TU;TV;>;>.T$U;", signatureRenamed);
}

void test6() {
final String signature = "<X:La/B$C<TY;>.D<TZ;>;:Le/F$G;>Lh/I$J;"; //a ClassSignature
final HashMap<String, String> map = new HashMap<>();
map.put("a/B$C$D", "o/P$Q$R");
map.put("e/F$G", "s/T$U");
map.put("h/I$J", "v/W$X");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("<X:Lo/P$Q<TY;>.R<TZ;>;:Ls/T$U;>Lv/W$X;", signatureRenamed);
}

void test7() {
final String signature = "<A:La/B$C;:Ld/E<TX;>.F<TY;>;:TZ;B:Ljava/lang/Thread;>(LX;TA;LA;)V^Ljava/lang/Exception;"; //a MethodSignature
final HashMap<String, String> map = new HashMap<>();
map.put("A", "P");
map.put("a/B$C", "s/T$U");
map.put("d/E$F", "v/W$X");
map.put("X", "V");
map.put("java/lang/Exception", "java/lang/RuntimeException");
final String signatureRenamed = SignatureAttribute.renameClass(signature, map);
assertEquals("<A:Ls/T$U;:Lv/W<TX;>.X<TY;>;:TZ;B:Ljava/lang/Thread;>(LV;TA;LP;)V^Ljava/lang/RuntimeException;", signatureRenamed);
}

public static void main(String[] s) {
new SignatureAttributeTest().test1();
new SignatureAttributeTest().test2();
new SignatureAttributeTest().test3();
new SignatureAttributeTest().test4();
new SignatureAttributeTest().test5();
new SignatureAttributeTest().test6();
new SignatureAttributeTest().test7();
}
}

0 comments on commit c8b54b3

Please sign in to comment.