fix: resolve rename of inner class using full name (#1997)
This commit is contained in:
@@ -4,7 +4,6 @@ import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -245,8 +244,12 @@ public class JadxArgs implements Closeable {
|
||||
return inputFiles;
|
||||
}
|
||||
|
||||
public void addInputFile(File inputFile) {
|
||||
this.inputFiles.add(inputFile);
|
||||
}
|
||||
|
||||
public void setInputFile(File inputFile) {
|
||||
this.inputFiles = Collections.singletonList(inputFile);
|
||||
addInputFile(inputFile);
|
||||
}
|
||||
|
||||
public void setInputFiles(List<File> inputFiles) {
|
||||
|
||||
@@ -23,9 +23,9 @@ public final class ClassInfo implements Comparable<ClassInfo> {
|
||||
@Nullable
|
||||
private ClassAliasInfo alias;
|
||||
|
||||
private ClassInfo(RootNode root, ArgType type) {
|
||||
private ClassInfo(RootNode root, ArgType type, boolean canBeInner) {
|
||||
this.type = type;
|
||||
splitAndApplyNames(root, type, root.getArgs().isMoveInnerClasses());
|
||||
splitAndApplyNames(root, type, canBeInner);
|
||||
}
|
||||
|
||||
public static ClassInfo fromType(RootNode root, ArgType type) {
|
||||
@@ -34,7 +34,8 @@ public final class ClassInfo implements Comparable<ClassInfo> {
|
||||
if (cls != null) {
|
||||
return cls;
|
||||
}
|
||||
ClassInfo newClsInfo = new ClassInfo(root, clsType);
|
||||
boolean canBeInner = root.getArgs().isMoveInnerClasses();
|
||||
ClassInfo newClsInfo = new ClassInfo(root, clsType, canBeInner);
|
||||
return root.getInfoStorage().putCls(newClsInfo);
|
||||
}
|
||||
|
||||
@@ -42,6 +43,10 @@ public final class ClassInfo implements Comparable<ClassInfo> {
|
||||
return fromType(root, ArgType.object(clsName));
|
||||
}
|
||||
|
||||
public static ClassInfo fromNameWithoutCache(RootNode root, String fullClsName, boolean canBeInner) {
|
||||
return new ClassInfo(root, ArgType.object(fullClsName), canBeInner);
|
||||
}
|
||||
|
||||
private static ArgType checkClassType(ArgType type) {
|
||||
if (type == null) {
|
||||
throw new JadxRuntimeException("Null class type");
|
||||
|
||||
@@ -624,32 +624,36 @@ public class ClassNode extends NotificationAttrNode
|
||||
*/
|
||||
@Override
|
||||
public void rename(String newName) {
|
||||
int lastDot = newName.lastIndexOf('.');
|
||||
if (lastDot == -1) {
|
||||
if (newName.indexOf('.') == -1) {
|
||||
clsInfo.changeShortName(newName);
|
||||
return;
|
||||
}
|
||||
if (clsInfo.isInner()) {
|
||||
addWarn("Can't change package for inner class: " + this + " to " + newName);
|
||||
return;
|
||||
}
|
||||
// full name provided
|
||||
ClassInfo newClsInfo = ClassInfo.fromNameWithoutCache(root, newName, clsInfo.isInner());
|
||||
// change class package
|
||||
String newPkg = newName.substring(0, lastDot);
|
||||
String newShortName = newName.substring(lastDot + 1);
|
||||
if (changeClassNodePackage(newPkg)) {
|
||||
clsInfo.changePkgAndName(newPkg, newShortName);
|
||||
} else {
|
||||
String newPkg = newClsInfo.getPackage();
|
||||
String newShortName = newClsInfo.getShortName();
|
||||
if (clsInfo.isInner()) {
|
||||
if (!newPkg.equals(clsInfo.getPackage())) {
|
||||
addWarn("Can't change package for inner class: " + this + " to " + newName);
|
||||
}
|
||||
clsInfo.changeShortName(newShortName);
|
||||
} else {
|
||||
if (changeClassNodePackage(newPkg)) {
|
||||
clsInfo.changePkgAndName(newPkg, newShortName);
|
||||
} else {
|
||||
clsInfo.changeShortName(newShortName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean changeClassNodePackage(String fullPkg) {
|
||||
if (clsInfo.isInner()) {
|
||||
throw new JadxRuntimeException("Can't change package for inner class: " + clsInfo);
|
||||
}
|
||||
if (fullPkg.equals(clsInfo.getAliasPkg())) {
|
||||
return false;
|
||||
}
|
||||
if (clsInfo.isInner()) {
|
||||
throw new JadxRuntimeException("Can't change package for inner class: " + clsInfo);
|
||||
}
|
||||
root.removeClsFromPackage(packageNode, this);
|
||||
packageNode = PackageNode.getForClass(root, fullPkg, this);
|
||||
root.sortPackages();
|
||||
|
||||
@@ -9,4 +9,7 @@ dependencies {
|
||||
exclude("org.ow2.asm:asm")
|
||||
exclude("net.fabricmc:tiny-remapper")
|
||||
}
|
||||
|
||||
testRuntimeOnly(project(":jadx-plugins:jadx-dex-input"))
|
||||
testRuntimeOnly(project(":jadx-plugins:jadx-smali-input"))
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package jadx.plugins.mappings;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.plugins.loader.JadxBasePluginLoader;
|
||||
import jadx.core.plugins.files.SingleDirFilesGetter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BaseRenameMappingsTest {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BaseRenameMappingsTest.class);
|
||||
|
||||
@TempDir
|
||||
Path testDir;
|
||||
|
||||
Path outputDir;
|
||||
|
||||
JadxArgs jadxArgs;
|
||||
|
||||
String testResDir = "";
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
outputDir = testDir.resolve("output");
|
||||
jadxArgs = new JadxArgs();
|
||||
jadxArgs.setOutDir(outputDir.toFile());
|
||||
jadxArgs.setFilesGetter(new SingleDirFilesGetter(testDir));
|
||||
jadxArgs.setPluginLoader(new JadxBasePluginLoader());
|
||||
}
|
||||
|
||||
public File loadResourceFile(String fileName) {
|
||||
String path = testResDir + File.separator + fileName;
|
||||
try {
|
||||
URL resource = getClass().getClassLoader().getResource(path);
|
||||
assertThat(resource).isNotNull();
|
||||
return new File(resource.getFile());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load resource file: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void printClassesCode(List<JavaClass> classes) {
|
||||
LOG.debug("Printing code for {} classes:", classes.size());
|
||||
for (JavaClass jCls : classes) {
|
||||
LOG.debug("Class: {}\n{}\n---\n", jCls.getFullName(), jCls.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package jadx.plugins.mappings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TestInnerClassRename extends BaseRenameMappingsTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
testResDir = "inner-cls-rename";
|
||||
jadxArgs.getInputFiles().add(loadResourceFile("base.smali"));
|
||||
jadxArgs.getInputFiles().add(loadResourceFile("inner.smali"));
|
||||
jadxArgs.setUserRenamesMappingsPath(loadResourceFile("enigma.mapping").toPath());
|
||||
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
|
||||
jadx.load();
|
||||
List<JavaClass> classes = jadx.getClasses();
|
||||
printClassesCode(classes);
|
||||
assertThat(classes).hasSize(1);
|
||||
JavaClass baseCls = classes.get(0);
|
||||
assertThat(baseCls.getName()).isEqualTo("BaseCls");
|
||||
List<JavaClass> innerClasses = baseCls.getInnerClasses();
|
||||
assertThat(innerClasses).hasSize(1);
|
||||
assertThat(innerClasses.get(0).getName()).isEqualTo("RenamedInner");
|
||||
assertThat(baseCls.getCode()).contains("class RenamedInner {");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
.class Ljadx/test/BaseCls;
|
||||
.super Ljava/lang/Object;
|
||||
@@ -0,0 +1,2 @@
|
||||
CLASS jadx/test/BaseCls
|
||||
CLASS Inner RenamedInner
|
||||
@@ -0,0 +1,2 @@
|
||||
.class Ljadx/test/BaseCls$Inner;
|
||||
.super Ljava/lang/Object;
|
||||
@@ -0,0 +1,13 @@
|
||||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user