Skip to content

Commit

Permalink
feat: improve better name algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
pubiqq committed Oct 10, 2024
1 parent 964bd62 commit 0a46eed
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private static String getBetterName(String currentName, String sourceName, UseSo
case ALWAYS:
return sourceName;
case IF_BETTER:
return BetterName.compareAndGet(sourceName, currentName);
return BetterName.getBetterClassName(sourceName, currentName);
case NEVER:
return currentName;
default:
Expand Down
78 changes: 78 additions & 0 deletions jadx-core/src/main/java/jadx/core/utils/BetterName.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,78 @@ public class BetterName {

private static final boolean DEBUG = false;

private static final double TOLERANCE = 0.001;

/**
* Compares two class names and returns the "better" one.
* If both names are equally good, {@code firstName} is returned.
*/
public static String getBetterClassName(String firstName, String secondName) {
return getBetterName(firstName, secondName);
}

/**
* Compares two resource names and returns the "better" one.
* If both names are equally good, {@code firstName} is returned.
*/
public static String getBetterResourceName(String firstName, String secondName) {
return getBetterName(firstName, secondName);
}

private static String getBetterName(String firstName, String secondName) {
if (Objects.equals(firstName, secondName)) {
return firstName;
}

if (StringUtils.isEmpty(firstName) || StringUtils.isEmpty(secondName)) {
return StringUtils.notEmpty(firstName)
? firstName
: secondName;
}

final var firstResult = analyze(firstName);
final var secondResult = analyze(secondName);

if (firstResult.digitCount != 0 || secondResult.digitCount != 0) {
final var firstRatio = (float) firstResult.digitCount / firstResult.length;
final var secondRatio = (float) secondResult.digitCount / secondResult.length;

if (Math.abs(secondRatio - firstRatio) >= TOLERANCE) {
return firstRatio <= secondRatio
? firstName
: secondName;
}
}

return firstResult.length >= secondResult.length
? firstName
: secondName;
}

private static AnalyzeResult analyze(String name) {
final var result = new AnalyzeResult();

StringUtils.visitCodePoints(name, cp -> {
if (Character.isDigit(cp)) {
result.digitCount++;
}

result.length++;
});

return result;
}

private static class AnalyzeResult {
private int length;
private int digitCount;
}

/**
* @deprecated Use {@link #getBetterClassName(String, String)} or
* {@link #getBetterResourceName(String, String)} instead.
*/
@Deprecated
public static String compareAndGet(String first, String second) {
if (Objects.equals(first, second)) {
return first;
Expand All @@ -32,6 +104,11 @@ public static String compareAndGet(String first, String second) {
return firstBetter ? first : second;
}

/**
* @deprecated This function is an implementation detail of deprecated
* {@link #compareAndGet(String, String)} and should not be used outside tests.
*/
@Deprecated
public static int calcRating(String str) {
int rating = str.length() * 3;
rating += differentCharsCount(str) * 20;
Expand All @@ -49,6 +126,7 @@ public static int calcRating(String str) {
return rating;
}

@Deprecated
private static int differentCharsCount(String str) {
String lower = str.toLowerCase(Locale.ROOT);
Set<Integer> chars = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ private String getResAlias(int resRef, String origKeyName, @Nullable FieldNode c
public static String getBetterName(ResourceNameSource nameSource, String resName, String codeName) {
switch (nameSource) {
case AUTO:
return BetterName.compareAndGet(resName, codeName);
return BetterName.getBetterResourceName(resName, codeName);
case RESOURCES:
return resName;
case CODE:
Expand Down
2 changes: 2 additions & 0 deletions jadx-core/src/test/java/jadx/core/utils/TestBetterName.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

public class TestBetterName {

@Deprecated
@Test
public void test() {
expectFirst("color_main", "t0");
expectFirst("done", "oOo0oO0o");
}

@Deprecated
private void expectFirst(String first, String second) {
String best = BetterName.compareAndGet(first, second);
assertThat(best)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package jadx.core.utils;

import org.assertj.core.api.AbstractStringAssert;
import org.junit.jupiter.api.Test;

import static jadx.core.utils.BetterName.getBetterClassName;
import static org.assertj.core.api.Assertions.assertThat;

public class TestGetBetterClassName {

@Test
public void testGoodNamesVsGeneratedAliases() {
assertThatBetterClassName("AppCompatButton", "C2404e2").isEqualTo("AppCompatButton");
assertThatBetterClassName("ContextThemeWrapper", "C2106b1").isEqualTo("ContextThemeWrapper");
assertThatBetterClassName("ListPopupWindow", "C2344a3").isEqualTo("ListPopupWindow");
}

@Test
public void testShortGoodNamesVsGeneratedAliases() {
assertThatBetterClassName("Room", "C2937kh").isEqualTo("Room");
assertThatBetterClassName("Fade", "C1428qi").isEqualTo("Fade");
assertThatBetterClassName("Scene", "C4063yi").isEqualTo("Scene");
}

@Test
public void testGoodNamesVsGeneratedAliasesWithPrefix() {
assertThatBetterClassName("AppCompatActivity", "ActivityC2646h0").isEqualTo("AppCompatActivity");
assertThatBetterClassName("PagerAdapter", "AbstractC3038lk").isEqualTo("PagerAdapter");
assertThatBetterClassName("Lazy", "InterfaceC6434a").isEqualTo("Lazy");
assertThatBetterClassName("MembersInjector", "InterfaceC6435b").isEqualTo("MembersInjector");
assertThatBetterClassName("Subscriber", "InterfaceC6439c").isEqualTo("Subscriber");
}

@Test
public void testGoodNamesWithDigitsVsGeneratedAliases() {
assertThatBetterClassName("ISO8061Formatter", "C1121uq4").isEqualTo("ISO8061Formatter");
assertThatBetterClassName("Jdk9Platform", "C1189rn6").isEqualTo("Jdk9Platform");
assertThatBetterClassName("WrappedDrawableApi14", "C2847i9").isEqualTo("WrappedDrawableApi14");
assertThatBetterClassName("WrappedDrawableApi21", "C2888j9").isEqualTo("WrappedDrawableApi21");
}

@Test
public void testShortNamesVsLongNames() {
assertThatBetterClassName("az", "Observer").isEqualTo("Observer");
assertThatBetterClassName("bb", "RenderEvent").isEqualTo("RenderEvent");
assertThatBetterClassName("aaaa", "FontUtils").isEqualTo("FontUtils");
}

/**
* Tests {@link BetterName#getBetterClassName(String, String)} on equally good names.
* In this case, according to the documentation, the method should return the first argument.
*
* @see BetterName#getBetterClassName(String, String)
*/
@Test
public void testEquallyGoodNames() {
assertThatBetterClassName("AAAA", "BBBB").isEqualTo("AAAA");
assertThatBetterClassName("BBBB", "AAAA").isEqualTo("BBBB");

assertThatBetterClassName("XYXYXY", "YZYZYZ").isEqualTo("XYXYXY");
assertThatBetterClassName("YZYZYZ", "XYXYXY").isEqualTo("YZYZYZ");
}

private AbstractStringAssert<?> assertThatBetterClassName(String firstName, String secondName) {
return assertThat(getBetterClassName(firstName, secondName));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package jadx.core.utils;

import org.assertj.core.api.AbstractStringAssert;
import org.junit.jupiter.api.Test;

import static jadx.core.utils.BetterName.getBetterResourceName;
import static org.assertj.core.api.Assertions.assertThat;

public class TestGetBetterResourceName {

@Test
public void testGoodNamesVsSyntheticNames() {
assertThatBetterResourceName("color_main", "t0").isEqualTo("color_main");
assertThatBetterResourceName("done", "oOo0oO0o").isEqualTo("done");
}

/**
* Tests {@link BetterName#getBetterResourceName(String, String)} on equally good names.
* In this case, according to the documentation, the method should return the first argument.
*
* @see BetterName#getBetterResourceName(String, String)
*/
@Test
public void testEquallyGoodNames() {
assertThatBetterResourceName("AAAA", "BBBB").isEqualTo("AAAA");
assertThatBetterResourceName("BBBB", "AAAA").isEqualTo("BBBB");

assertThatBetterResourceName("Theme.AppCompat.Light", "Theme_AppCompat_Light")
.isEqualTo("Theme.AppCompat.Light");
assertThatBetterResourceName("Theme_AppCompat_Light", "Theme.AppCompat.Light")
.isEqualTo("Theme_AppCompat_Light");
}

private AbstractStringAssert<?> assertThatBetterResourceName(String firstName, String secondName) {
return assertThat(getBetterResourceName(firstName, secondName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public void testNeverUseSourceName() {
.containsOne("class b {");
}

@Test
public void testIfBetterUseSourceName() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.IF_BETTER);
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class a {");
}

@Test
public void testAlwaysUseSourceName() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.ALWAYS);
Expand All @@ -36,6 +44,17 @@ public void testNeverUseSourceNameWithDeobf() {
.containsOne("/* compiled from: a.java */");
}

@Test
public void testIfBetterUseSourceNameWithDeobf() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.IF_BETTER);
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class a {")
.containsOne("/* compiled from: a.java */");
}

@Test
public void testAlwaysUseSourceNameWithDeobf() {
args.setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias.ALWAYS);
Expand Down Expand Up @@ -65,6 +84,18 @@ public void testDeprecatedUseSourceName() {
.containsOne("class a {");
}

@Test
public void testDeprecatedDontUseSourceNameWithDeobf() {
// noinspection deprecation
args.setUseSourceNameAsClassAlias(false);
enableDeobfuscation();
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class C0000b {")
.containsOne("/* compiled from: a.java */");
}

@Test
public void testDeprecatedUseSourceNameWithDeobf() {
// noinspection deprecation
Expand All @@ -73,7 +104,7 @@ public void testDeprecatedUseSourceNameWithDeobf() {
args.setDeobfuscationMinLength(100); // rename everything
assertThat(searchCls(loadFromSmaliFiles(), "b"))
.code()
.containsOne("class C0000b {")
.containsOne("class a {")
.containsOne("/* compiled from: a.java */");
}
}

0 comments on commit 0a46eed

Please sign in to comment.