Skip to content
Open
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 @@ -40,17 +40,17 @@ public void transform(@Nonnull JvmTransformerContext context, @Nonnull Workspace
IntermediateMappings mappings = context.getMappings();
String ownerName = initialClassState.getName();
if (ILLEGAL_NAME_FILTER.shouldMapClass(initialClassState) && mappings.getMappedClassName(ownerName) == null)
mappings.addClass(ownerName, NAME_GENERATOR.mapClass(initialClassState));
mappings.addClass(ownerName, NAME_GENERATOR.mapClass(initialClassState, ILLEGAL_NAME_FILTER.shouldMapPackage(initialClassState)));
for (FieldMember field : initialClassState.getFields()) {
String fieldDesc = field.getDescriptor();
String fieldName = field.getName();
if (ILLEGAL_NAME_FILTER.shouldMapField(initialClassState, field) && mappings.getMappedFieldName(ownerName, fieldDesc, fieldName) == null)
if (ILLEGAL_NAME_FILTER.shouldMapField(initialClassState, field) && mappings.getMappedFieldName(ownerName, fieldName, fieldDesc) == null)
mappings.addField(ownerName, fieldDesc, field.getName(), NAME_GENERATOR.mapField(initialClassState, field));
}
for (MethodMember method : initialClassState.getMethods()) {
String methodDesc = method.getDescriptor();
String methodName = method.getName();
if (ILLEGAL_NAME_FILTER.shouldMapMethod(initialClassState, method) && mappings.getMappedMethodName(ownerName, methodDesc, methodName) == null)
if (ILLEGAL_NAME_FILTER.shouldMapMethod(initialClassState, method) && mappings.getMappedMethodName(ownerName, methodName, methodDesc) == null)
mappings.addMethod(ownerName, methodDesc, method.getName(), NAME_GENERATOR.mapMethod(initialClassState, method));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ private void generateFamilyMappings(@Nonnull MappingsAdapter mappings, @Nonnull

// Add mapping.
String name = vertex.getName();
String mapped = generator.mapClass(classInfo);
String mapped = generator.mapClass(classInfo, filter.shouldMapPackage(classInfo));
mappings.addClass(name, mapped);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
(namePredicate.match(info.getName()));
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
return shouldMapClass(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
// Consider owner type, we do not want to map fields if they are outside the inclusion filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
String packageName = info.getPackageName();
if (packageName != null && containsKeyword(packageName))
return true;
return super.shouldMapPackage(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember info) {
if (containsKeyword(info.getName()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
String packageName = info.getPackageName();
if (targetClasses && packageName != null && shouldMap(packageName))
return true;
return super.shouldMapPackage(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
if (targetFields && shouldMap(field))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
return shouldMapClass(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
if (targetFields && field.hasAnyModifiers(flags))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
return shouldMapClass(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
if (fieldPredicate != null && fieldPredicate.match(field.getName()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
String packageName = info.getPackageName();
if (packageName != null && shouldMap(packageName))
return true;
return super.shouldMapPackage(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
if (shouldMap(field))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ else if (name.endsWith("module-info"))
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
String packageName = info.getPackageName();
if (packageName != null && isInvalidName(packageName))
return true;
return super.shouldMapPackage(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember info) {
if (isInvalidName(info.getName()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return super.shouldMapClass(info);
}

@Override
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
String packageName = info.getPackageName();
if (packageName != null && shouldMap(packageName))
return true;
return super.shouldMapPackage(info);
}

@Override
public boolean shouldMapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
if (shouldMap(field))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
return next != null && next.shouldMapClass(info);
}

/**
* @param info Class whose package should be checked.
* @return {@code true} when a remapped class should also be moved to a different package.
*/
public boolean shouldMapPackage(@Nonnull ClassInfo info) {
if (defaultMap)
return next == null || next.shouldMapPackage(info);
else
return next != null && next.shouldMapPackage(info);
}

/**
* @param owner
* Class the field is defined in.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ public AlphabetNameGenerator(@Nonnull String alphabet, int length) {

@Nonnull
private String name(@Nullable String original) {
return name(original, null);
}

@Nonnull
private String name(@Nullable String original, @Nullable String packageName) {
int seed = original == null ? alphabet.hashCode() : original.hashCode();
String name = StringUtil.generateName(alphabet, length, seed);
String simpleName = StringUtil.generateName(alphabet, length, seed);
String name = packageName == null ? simpleName : packageName + "/" + simpleName;
if (workspace != null) {
while (workspace.findClass(name) != null)
name = StringUtil.generateName(alphabet, length, seed++);
name = packageName == null ?
StringUtil.generateName(alphabet, length, ++seed) :
packageName + "/" + StringUtil.generateName(alphabet, length, ++seed);
}
return name;
}
Expand All @@ -50,11 +58,21 @@ public void setWorkspace(@Nullable Workspace workspace) {
@Nonnull
@Override
public String mapClass(@Nonnull ClassInfo info) {
return mapClass(info, true);
}

@Nonnull
@Override
public String mapClass(@Nonnull ClassInfo info, boolean mapPackage) {
if (info.isInDefaultPackage())
return name(info.getName());

// Ensure classes in the same package are kept together
return name(info.getPackageName()) + "/" + name(info.getName());
if (!mapPackage)
return name(info.getName(), info.getPackageName());

// Ensure classes in the same package are kept together.
String mappedPackage = name(info.getPackageName());
return name(info.getName(), mappedPackage);
}

@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ public class IncrementingNameGenerator implements DeconflictingNameGenerator {
private long varIndex = 1;

@Nonnull
private String nextClassName() {
return "mapped/Class" + classIndex++;
private String nextClassName(@Nullable String packageName) {
String simpleName = "Class" + classIndex++;
if (packageName == null || packageName.isEmpty())
return simpleName;
return packageName + "/" + simpleName;
}

@Nonnull
Expand All @@ -49,10 +52,17 @@ public void setWorkspace(@Nullable Workspace workspace) {
@Nonnull
@Override
public String mapClass(@Nonnull ClassInfo info) {
String name = nextClassName();
return mapClass(info, true);
}

@Nonnull
@Override
public String mapClass(@Nonnull ClassInfo info, boolean mapPackage) {
String packageName = mapPackage ? "mapped" : info.getPackageName();
String name = nextClassName(packageName);
if (workspace != null) {
while (workspace.findClass(name) != null)
name = nextClassName();
name = nextClassName(packageName);
}
return name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ public interface NameGenerator {
@Nonnull
String mapClass(@Nonnull ClassInfo info);

/**
* @param info
* Class to rename.
* @param mapPackage
* {@code true} to allow remapping the package path.
* {@code false} to keep the class in its current package.
*
* @return New class name.
*/
@Nonnull
default String mapClass(@Nonnull ClassInfo info, boolean mapPackage) {
String mappedName = mapClass(info);
if (mapPackage || info.isInDefaultPackage())
return mappedName;

String packageName = info.getPackageName();
int separatorIndex = mappedName.lastIndexOf('/');
String simpleName = separatorIndex >= 0 ? mappedName.substring(separatorIndex + 1) : mappedName;
return packageName + "/" + simpleName;
}

/**
* @param owner
* Class the field is defined in.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import software.coley.recaf.services.mapping.gen.filter.IncludeNonJavaIdentifierNameFilter;
import software.coley.recaf.services.mapping.gen.filter.IncludeWhitespaceNameFilter;
import software.coley.recaf.services.mapping.gen.filter.NameGeneratorFilter;
import software.coley.recaf.services.mapping.gen.naming.AlphabetNameGenerator;
import software.coley.recaf.services.mapping.gen.naming.IncrementingNameGenerator;
import software.coley.recaf.services.mapping.gen.naming.NameGenerator;
import software.coley.recaf.services.search.match.StringPredicate;
import software.coley.recaf.services.search.match.StringPredicateProvider;
Expand Down Expand Up @@ -444,6 +446,10 @@ void testIncludeKeywordNameFilter() {
// Edge case: 'package-info' and 'module-info' are not keywords, but they contain the keyword 'package' and 'module' respectively.
assertFalse(filter.shouldMapClass(new StubClassInfo("package-info")));
assertFalse(filter.shouldMapClass(new StubClassInfo("module-info")));

// Only remap the package when the package path itself is the problem.
assertFalse(filter.shouldMapPackage(new StubClassInfo("com/example/class")));
assertTrue(filter.shouldMapPackage(new StubClassInfo("com/class/Example")));
}

@Test
Expand All @@ -458,6 +464,8 @@ void testIncludeWhitespaceNameFilter() {
assertTrue(filter.shouldMapClass(new StubClassInfo("com/_/Example".replace("_", space))));
assertTrue(filter.shouldMapClass(new StubClassInfo("_/example/Example".replace("_", space))));
assertTrue(filter.shouldMapClass(new StubClassInfo("_".replace("_", space))));
assertFalse(filter.shouldMapPackage(new StubClassInfo("com/example/Bad_".replace("_", space))));
assertTrue(filter.shouldMapPackage(new StubClassInfo("com/_/Example".replace("_", space))));
}
}

Expand All @@ -477,6 +485,10 @@ void testIncludeNonAsciiNameFilter() {

// Technically in the ASCII set, but still non-ASCII in the sense of Java identifiers, should be mapped.
assertTrue(filter.shouldMapClass(new StubClassInfo("\0")));

// Only remap the package when the package path itself is the problem.
assertFalse(filter.shouldMapPackage(new StubClassInfo("com/example/Exämple")));
assertTrue(filter.shouldMapPackage(new StubClassInfo("cöm/example/Example")));
}

@Test
Expand All @@ -497,13 +509,40 @@ void testIncludeNonJavaIdentifierNameFilter() {
// A package part that starts with a number is not valid, should be mapped.
assertTrue(filter.shouldMapClass(new StubClassInfo("com/1A/Example")));
assertFalse(filter.shouldMapClass(new StubClassInfo("com/A1/Example")));
assertFalse(filter.shouldMapPackage(new StubClassInfo("com/example/1Example")));
assertTrue(filter.shouldMapPackage(new StubClassInfo("com/1A/Example")));

// Edge case: 'package-info' and 'module-info' are not valid Java identifiers, but they are exempt
// from this filter since they have special meaning in Java.
assertFalse(filter.shouldMapClass(new StubClassInfo("package-info")));
assertFalse(filter.shouldMapClass(new StubClassInfo("module-info")));
}

@Test
void testIncrementingGeneratorPreservesLegalPackageForIllegalClassName() {
IncludeNonJavaIdentifierNameFilter filter = new IncludeNonJavaIdentifierNameFilter(null);
ClassInfo info = new StubClassInfo("pack/pack/1");
IncrementingNameGenerator generator = new IncrementingNameGenerator();

assertTrue(filter.shouldMapClass(info));
assertFalse(filter.shouldMapPackage(info));
assertEquals("pack/pack/Class1", generator.mapClass(info, filter.shouldMapPackage(info)));
}

@Test
void testAlphabetGeneratorPreservesLegalPackageForIllegalClassName() {
IncludeNonJavaIdentifierNameFilter filter = new IncludeNonJavaIdentifierNameFilter(null);
ClassInfo info = new StubClassInfo("pack/pack/1");
AlphabetNameGenerator generator = new AlphabetNameGenerator("abcdefghijklmnopqrstuvwxyz", 3);

assertTrue(filter.shouldMapClass(info));
assertFalse(filter.shouldMapPackage(info));

String mapped = generator.mapClass(info, filter.shouldMapPackage(info));
assertTrue(mapped.startsWith("pack/pack/"));
assertNotEquals(info.getName(), mapped);
}

@Test
void testIncludeLongNameFilter() {
// Anything > 100 characters
Expand Down