refactor: use package nodes in api and ui

This commit is contained in:
Skylot
2022-08-10 13:35:56 +01:00
parent d4927db52b
commit cb1f3e9843
30 changed files with 832 additions and 518 deletions
@@ -6,7 +6,6 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -45,6 +44,7 @@ import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.PackageNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.SaveCode;
import jadx.core.export.ExportGradleProject;
@@ -444,25 +444,7 @@ public final class JadxDecompiler implements IJadxDecompiler, Closeable {
}
public List<JavaPackage> getPackages() {
List<JavaClass> classList = getClasses();
if (classList.isEmpty()) {
return Collections.emptyList();
}
Map<String, List<JavaClass>> map = new HashMap<>();
for (JavaClass javaClass : classList) {
String pkg = javaClass.getPackage();
List<JavaClass> clsList = map.computeIfAbsent(pkg, k -> new ArrayList<>());
clsList.add(javaClass);
}
List<JavaPackage> packages = new ArrayList<>(map.size());
for (Map.Entry<String, List<JavaClass>> entry : map.entrySet()) {
packages.add(new JavaPackage(entry.getKey(), entry.getValue()));
}
Collections.sort(packages);
for (JavaPackage pkg : packages) {
pkg.getClasses().sort(Comparator.comparing(JavaClass::getName, String.CASE_INSENSITIVE_ORDER));
}
return Collections.unmodifiableList(packages);
return Utils.collectionMap(root.getPackages(), this::convertPackageNode);
}
public int getErrorsCount() {
@@ -545,6 +527,26 @@ public final class JadxDecompiler implements IJadxDecompiler, Closeable {
return javaMethod;
}
@ApiStatus.Internal
synchronized JavaPackage convertPackageNode(PackageNode pkg) {
JavaPackage foundPkg = pkg.getJavaNode();
if (foundPkg != null) {
return foundPkg;
}
List<JavaClass> clsList = Utils.collectionMap(pkg.getClasses(), this::convertClassNode);
int subPkgsCount = pkg.getSubPackages().size();
List<JavaPackage> subPkgs = subPkgsCount == 0 ? Collections.emptyList() : new ArrayList<>(subPkgsCount);
JavaPackage javaPkg = new JavaPackage(pkg, clsList, subPkgs);
if (subPkgsCount != 0) {
// add subpackages after parent to avoid endless recursion
for (PackageNode subPackage : pkg.getSubPackages()) {
subPkgs.add(convertPackageNode(subPackage));
}
}
pkg.setJavaNode(javaPkg);
return javaPkg;
}
@Nullable
public JavaClass searchJavaClassByOrigFullName(String fullName) {
return getRoot().getClasses().stream()
@@ -18,8 +18,7 @@ public interface JavaNode {
List<JavaNode> getUseIn();
default void removeAlias() {
}
void removeAlias();
boolean isOwnCodeAnnotation(ICodeAnnotation ann);
}
@@ -1,36 +1,85 @@
package jadx.api;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import jadx.api.metadata.ICodeAnnotation;
import jadx.core.dex.info.PackageInfo;
import jadx.core.dex.nodes.PackageNode;
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
private final String name;
private final PackageNode pkgNode;
private final List<JavaClass> classes;
private final List<JavaPackage> subPkgs;
JavaPackage(String name, List<JavaClass> classes) {
this.name = name;
JavaPackage(PackageNode pkgNode, List<JavaClass> classes, List<JavaPackage> subPkgs) {
this.pkgNode = pkgNode;
this.classes = classes;
this.subPkgs = subPkgs;
}
@Override
public String getName() {
return name;
return pkgNode.getAliasPkgInfo().getName();
}
@Override
public String getFullName() {
// TODO: store full package name
return name;
return pkgNode.getAliasPkgInfo().getFullName();
}
public String getRawName() {
return pkgNode.getPkgInfo().getName();
}
public String getRawFullName() {
return pkgNode.getPkgInfo().getFullName();
}
public List<JavaPackage> getSubPackages() {
return subPkgs;
}
public List<JavaClass> getClasses() {
return classes;
}
public boolean isRoot() {
return pkgNode.isRoot();
}
public boolean isLeaf() {
return pkgNode.isLeaf();
}
public boolean isDefault() {
return getFullName().isEmpty();
}
public void rename(String alias) {
pkgNode.rename(alias);
}
@Override
public void removeAlias() {
pkgNode.removeAlias();
}
public boolean isParentRenamed() {
PackageInfo parent = pkgNode.getPkgInfo().getParentPkg();
PackageInfo aliasParent = pkgNode.getAliasPkgInfo().getParentPkg();
return !Objects.equals(parent, aliasParent);
}
@Internal
public PackageNode getPkgNode() {
return pkgNode;
}
@Override
public JavaClass getDeclaringClass() {
return null;
@@ -48,7 +97,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
@Override
public List<JavaNode> getUseIn() {
return Collections.emptyList();
List<JavaNode> list = new ArrayList<>();
addUseIn(list);
return list;
}
public void addUseIn(List<JavaNode> list) {
list.addAll(classes);
for (JavaPackage subPkg : subPkgs) {
subPkg.addUseIn(list);
}
}
@Override
@@ -58,7 +116,7 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
@Override
public int compareTo(@NotNull JavaPackage o) {
return name.compareTo(o.name);
return pkgNode.compareTo(o.pkgNode);
}
@Override
@@ -70,16 +128,16 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
return false;
}
JavaPackage that = (JavaPackage) o;
return name.equals(that.name);
return pkgNode.equals(that.pkgNode);
}
@Override
public int hashCode() {
return name.hashCode();
return pkgNode.hashCode();
}
@Override
public String toString() {
return name;
return pkgNode.toString();
}
}
@@ -71,6 +71,10 @@ public class JavaVariable implements JavaNode {
return Collections.singletonList(mth);
}
@Override
public void removeAlias() {
}
@Override
public boolean isOwnCodeAnnotation(ICodeAnnotation ann) {
if (ann.getAnnType() == ICodeAnnotation.AnnType.VAR_REF) {
@@ -85,4 +85,12 @@ public class JadxCodeRename implements ICodeRename {
public int hashCode() {
return 31 * getNodeRef().hashCode() + Objects.hashCode(getCodeRef());
}
@Override
public String toString() {
return "JadxCodeRename{" + nodeRef
+ ", codeRef=" + codeRef
+ ", newName='" + newName + '\''
+ '}';
}
}
@@ -11,7 +11,7 @@ import static jadx.core.utils.StringUtils.notEmpty;
public class NameMapper {
private static final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile(
public static final Pattern VALID_JAVA_IDENTIFIER = Pattern.compile(
"\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
private static final Pattern VALID_JAVA_FULL_IDENTIFIER = Pattern.compile(
@@ -7,6 +7,7 @@ import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.api.JavaPackage;
import jadx.core.dex.info.PackageInfo;
public class PackageNode implements IPackageUpdate, IDexNode, Comparable<PackageNode> {
@@ -19,6 +20,8 @@ public class PackageNode implements IPackageUpdate, IDexNode, Comparable<Package
private PackageInfo aliasPkgInfo;
private JavaPackage javaNode;
public static PackageNode getForClass(RootNode root, String fullPkg, ClassNode cls) {
PackageNode pkg = getOrBuild(root, fullPkg);
pkg.getClasses().add(cls);
@@ -144,6 +147,10 @@ public class PackageNode implements IPackageUpdate, IDexNode, Comparable<Package
return !Objects.equals(pkgInfo.getParentPkg(), aliasPkgInfo.getParentPkg());
}
public void removeAlias() {
aliasPkgInfo = pkgInfo;
}
public PackageNode getParentPkg() {
return parentPkg;
}
@@ -168,6 +175,14 @@ public class PackageNode implements IPackageUpdate, IDexNode, Comparable<Package
return classes;
}
public JavaPackage getJavaNode() {
return javaNode;
}
public void setJavaNode(JavaPackage javaNode) {
this.javaNode = javaNode;
}
@Override
public String typeName() {
return "package";
@@ -302,6 +302,9 @@ public class RootNode implements IRootNode {
}
}
classes.forEach(ClassNode::updateParentClass);
for (PackageNode pkg : packages) {
pkg.getClasses().removeIf(ClassNode::isInner);
}
}
public void mergePasses(Map<JadxPassType, List<JadxPass>> customPasses) {
@@ -225,7 +225,7 @@ public class DebugUtils {
}
public static void printStackTrace(String label) {
LOG.debug("StackTrace: {}\n{}", label, Utils.getStackTrace(new Exception()));
LOG.debug("StackTrace: {}\n{}", label, Utils.getFullStackTrace(new Exception()));
}
public static void printMethodOverrideTop(RootNode root) {
@@ -3,10 +3,12 @@ package jadx.core.utils;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -14,6 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
@@ -116,13 +119,23 @@ public class Utils {
return sb.toString();
}
public static String getFullStackTrace(Throwable throwable) {
return getStackTrace(throwable, false);
}
public static String getStackTrace(Throwable throwable) {
return getStackTrace(throwable, true);
}
private static String getStackTrace(Throwable throwable, boolean filter) {
if (throwable == null) {
return "";
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
filterRecursive(throwable);
if (filter) {
filterRecursive(throwable);
}
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
@@ -345,6 +358,27 @@ public class Utils {
return map;
}
/**
* Simple DFS visit for tree (cycles not allowed)
*/
public static <T> void treeDfsVisit(T root, Function<T, List<T>> childrenProvider, Consumer<T> visitor) {
multiRootTreeDfsVisit(Collections.singletonList(root), childrenProvider, visitor);
}
public static <T> void multiRootTreeDfsVisit(List<T> roots, Function<T, List<T>> childrenProvider, Consumer<T> visitor) {
Deque<T> queue = new ArrayDeque<>(roots);
while (true) {
T current = queue.pollLast();
if (current == null) {
return;
}
visitor.accept(current);
for (T child : childrenProvider.apply(current)) {
queue.addLast(child);
}
}
}
@Nullable
public static <T> T getOne(@Nullable List<T> list) {
if (list == null || list.size() != 1) {