提交 e2b42804 编写于 作者: S Skylot

fix: resolve several issues with package rename and class reload (#987)

上级 0f6e942c
......@@ -57,7 +57,7 @@ public final class JavaClass implements JavaNode {
cls.decompile();
}
public synchronized void refresh() {
public synchronized void reload() {
listsLoaded = false;
cls.reloadCode();
}
......
......@@ -31,6 +31,12 @@ public final class ProcessClass {
}
synchronized (cls.getClassInfo()) {
try {
if (cls.contains(AFlag.CLASS_DEEP_RELOAD)) {
cls.remove(AFlag.CLASS_DEEP_RELOAD);
cls.unload();
cls.deepUnload();
cls.root().runPreDecompileStageForClass(cls);
}
if (codegen) {
if (cls.getState() == GENERATED_AND_UNLOADED) {
// allow to run code generation again
......
......@@ -22,9 +22,6 @@ public enum AFlag {
HIDDEN, // instruction used inside other instruction but not listed in args
RESTART_CODEGEN, // codegen must be executed again
RELOAD_AT_CODEGEN_STAGE, // class can't be analyzed at 'process' stage => unload before 'codegen' stage
DONT_RENAME, // do not rename during deobfuscation
ADDED_TO_REGION,
......@@ -76,5 +73,10 @@ public enum AFlag {
REQUEST_IF_REGION_OPTIMIZE, // run if region visitor again
// Class processing flags
RESTART_CODEGEN, // codegen must be executed again
RELOAD_AT_CODEGEN_STAGE, // class can't be analyzed at 'process' stage => unload before 'codegen' stage
CLASS_DEEP_RELOAD, // perform deep class unload (reload) before process
DONT_UNLOAD_CLASS, // don't unload class after code generation (only for tests and debug!)
}
......@@ -225,10 +225,8 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
return decompile(true);
}
public synchronized ICodeInfo reloadCode() {
unload();
deepUnload();
root.runPreDecompileStageForClass(this);
public ICodeInfo reloadCode() {
add(AFlag.CLASS_DEEP_RELOAD);
return decompile(false);
}
......
......@@ -8,6 +8,7 @@ import java.util.Objects;
import jadx.core.clsp.ClspClass;
import jadx.core.clsp.ClspMethod;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.instructions.args.ArgType;
......@@ -33,20 +34,25 @@ public class OverrideMethodVisitor extends AbstractVisitor {
public boolean visit(ClassNode cls) throws JadxException {
List<ArgType> superTypes = collectSuperTypes(cls);
for (MethodNode mth : cls.getMethods()) {
if (mth.isConstructor() || mth.getAccessFlags().isStatic()) {
continue;
}
String signature = mth.getMethodInfo().makeSignature(false);
List<IMethodDetails> overrideList = collectOverrideMethods(cls, superTypes, signature);
if (!overrideList.isEmpty()) {
mth.addAttr(new MethodOverrideAttr(overrideList));
fixMethodReturnType(mth, overrideList, superTypes);
fixMethodArgTypes(mth, overrideList, superTypes);
}
processMth(cls, superTypes, mth);
}
return true;
}
private void processMth(ClassNode cls, List<ArgType> superTypes, MethodNode mth) {
if (mth.isConstructor() || mth.getAccessFlags().isStatic()) {
return;
}
mth.remove(AType.METHOD_OVERRIDE);
String signature = mth.getMethodInfo().makeSignature(false);
List<IMethodDetails> overrideList = collectOverrideMethods(cls, superTypes, signature);
if (!overrideList.isEmpty()) {
mth.addAttr(new MethodOverrideAttr(overrideList));
fixMethodReturnType(mth, overrideList, superTypes);
fixMethodArgTypes(mth, overrideList, superTypes);
}
}
private List<IMethodDetails> collectOverrideMethods(ClassNode cls, List<ArgType> superTypes, String signature) {
List<IMethodDetails> overrideList = new ArrayList<>();
for (ArgType superType : superTypes) {
......
......@@ -59,8 +59,8 @@ public class JClass extends JLoadableNode {
update();
}
public synchronized void refresh() {
cls.refresh();
public synchronized void reload() {
cls.reload();
loaded = true;
update();
cls.unload();
......
package jadx.gui.treemodel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.*;
import org.jetbrains.annotations.NotNull;
import jadx.api.JavaClass;
import jadx.api.JavaPackage;
import jadx.core.utils.Utils;
import jadx.gui.JadxWrapper;
import jadx.gui.utils.UiUtils;
......@@ -17,42 +18,41 @@ public class JPackage extends JNode implements Comparable<JPackage> {
private static final ImageIcon PACKAGE_ICON = UiUtils.openIcon("package_obj");
private final String fullName;
private String fullName;
private String name;
private boolean enabled;
private final List<JClass> classes;
private final List<JPackage> innerPackages = new ArrayList<>();
private List<JClass> classes;
private List<JPackage> innerPackages;
public JPackage(JavaPackage pkg, JadxWrapper wrapper) {
this.fullName = pkg.getName();
this.name = pkg.getName();
setEnabled(wrapper);
List<JavaClass> javaClasses = pkg.getClasses();
this.classes = new ArrayList<>(javaClasses.size());
for (JavaClass javaClass : javaClasses) {
classes.add(new JClass(javaClass));
}
this(pkg.getName(), pkg.getName(),
isPkgEnabled(wrapper, pkg.getName()),
Utils.collectionMap(pkg.getClasses(), JClass::new),
new ArrayList<>());
update();
}
public JPackage(String name, JadxWrapper wrapper) {
this.fullName = name;
this.name = name;
setEnabled(wrapper);
this.classes = new ArrayList<>();
public JPackage(String fullName, JadxWrapper wrapper) {
this(fullName, fullName, isPkgEnabled(wrapper, fullName), new ArrayList<>(), new ArrayList<>());
}
public JPackage(String fullName, String name) {
this(fullName, name, true, Collections.emptyList(), Collections.emptyList());
}
private JPackage(String fullName, String name, boolean enabled, List<JClass> classes, List<JPackage> innerPackages) {
this.fullName = fullName;
this.name = name;
this.classes = new ArrayList<>();
this.enabled = enabled;
this.classes = classes;
this.innerPackages = innerPackages;
}
private void setEnabled(JadxWrapper wrapper) {
private static boolean isPkgEnabled(JadxWrapper wrapper, String fullPkgName) {
List<String> excludedPackages = wrapper.getExcludedPackages();
this.enabled = excludedPackages.isEmpty()
return excludedPackages.isEmpty()
|| excludedPackages.stream().filter(p -> !p.isEmpty())
.noneMatch(p -> name.equals(p) || name.startsWith(p + '.'));
.noneMatch(p -> fullPkgName.equals(p) || fullPkgName.startsWith(p + '.'));
}
public final void update() {
......@@ -78,7 +78,13 @@ public class JPackage extends JNode implements Comparable<JPackage> {
return fullName;
}
public void setName(String name) {
public void updateBothNames(String fullName, String name, JadxWrapper wrapper) {
this.fullName = fullName;
this.name = name;
this.enabled = isPkgEnabled(wrapper, fullName);
}
public void updateName(String name) {
this.name = name;
}
......@@ -86,10 +92,18 @@ public class JPackage extends JNode implements Comparable<JPackage> {
return innerPackages;
}
public void setInnerPackages(List<JPackage> innerPackages) {
this.innerPackages = innerPackages;
}
public List<JClass> getClasses() {
return classes;
}
public void setClasses(List<JClass> classes) {
this.classes = classes;
}
@Override
public Icon getIcon() {
return PACKAGE_ICON;
......
......@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -62,16 +61,16 @@ public class JSources extends JNode {
do {
repeat = false;
for (JPackage pkg : pkgMap.values()) {
if (pkg.getInnerPackages().size() == 1 && pkg.getClasses().isEmpty()) {
JPackage innerPkg = pkg.getInnerPackages().get(0);
pkg.getInnerPackages().clear();
pkg.getInnerPackages().addAll(innerPkg.getInnerPackages());
pkg.getClasses().addAll(innerPkg.getClasses());
pkg.setName(pkg.getName() + '.' + innerPkg.getName());
innerPkg.getInnerPackages().clear();
innerPkg.getClasses().clear();
List<JPackage> innerPackages = pkg.getInnerPackages();
if (innerPackages.size() == 1 && pkg.getClasses().isEmpty()) {
JPackage innerPkg = innerPackages.get(0);
pkg.setInnerPackages(innerPkg.getInnerPackages());
pkg.setClasses(innerPkg.getClasses());
String innerName = '.' + innerPkg.getName();
pkg.updateBothNames(pkg.getFullName() + innerName, pkg.getName() + innerName, wrapper);
innerPkg.setInnerPackages(Collections.emptyList());
innerPkg.setClasses(Collections.emptyList());
repeat = true;
break;
}
......@@ -79,12 +78,8 @@ public class JSources extends JNode {
} while (repeat);
// remove empty packages
for (Iterator<Map.Entry<String, JPackage>> it = pkgMap.entrySet().iterator(); it.hasNext();) {
JPackage pkg = it.next().getValue();
if (pkg.getInnerPackages().isEmpty() && pkg.getClasses().isEmpty()) {
it.remove();
}
}
pkgMap.values().removeIf(pkg -> pkg.getInnerPackages().isEmpty() && pkg.getClasses().isEmpty());
// use identity set for collect inner packages
Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<>());
for (JPackage pkg : pkgMap.values()) {
......@@ -102,7 +97,7 @@ public class JSources extends JNode {
}
private void addPackage(Map<String, JPackage> pkgs, JPackage pkg) {
String pkgName = pkg.getName();
String pkgName = pkg.getFullName();
JPackage replaced = pkgs.put(pkgName, pkg);
if (replaced != null) {
pkg.getInnerPackages().addAll(replaced.getInnerPackages());
......@@ -112,7 +107,7 @@ public class JSources extends JNode {
if (dot > 0) {
String prevPart = pkgName.substring(0, dot);
String shortName = pkgName.substring(dot + 1);
pkg.setName(shortName);
pkg.updateName(shortName);
JPackage prevPkg = pkgs.get(prevPart);
if (prevPkg == null) {
prevPkg = new JPackage(prevPart, wrapper);
......
......@@ -3,12 +3,11 @@ package jadx.gui.ui;
import java.util.Arrays;
import java.util.List;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.*;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JClass;
......@@ -18,6 +17,8 @@ import jadx.gui.utils.NLS;
class JPackagePopupMenu extends JPopupMenu {
private static final long serialVersionUID = -7781009781149224131L;
private static final Logger LOG = LoggerFactory.getLogger(JPackagePopupMenu.class);
private final transient MainWindow mainWindow;
public JPackagePopupMenu(MainWindow mainWindow, JPackage pkg) {
......@@ -32,8 +33,8 @@ class JPackagePopupMenu extends JPopupMenu {
@Nullable
private JMenuItem makeRenameMenuItem(JPackage pkg) {
List<String> aliasParts = splitPackage(pkg.getName());
int count = aliasParts.size();
List<String> aliasShortParts = splitPackage(pkg.getName());
int count = aliasShortParts.size();
if (count == 0) {
return null;
}
......@@ -41,20 +42,20 @@ class JPackagePopupMenu extends JPopupMenu {
if (rawPackage == null) {
return null;
}
List<String> aliasParts = splitPackage(pkg.getFullName());
List<String> rawParts = splitPackage(rawPackage); // can be longer then alias parts
int start = aliasParts.size() - count;
if (count == 1) {
// single case => no submenu
String aliasPkg = aliasParts.get(0);
JPackage renamePkg = new JPackage(rawPackage, aliasPkg);
JPackage renamePkg = new JPackage(concat(rawParts, start), aliasParts.get(start));
JMenuItem pkgItem = new JMenuItem(NLS.str("popup.rename"));
pkgItem.addActionListener(e -> rename(renamePkg));
return pkgItem;
}
List<String> rawParts = splitPackage(rawPackage); // can be longer then alias
JMenuItem renameSubMenu = new JMenu(NLS.str("popup.rename"));
for (int i = 0; i < count; i++) {
String rawPkg = concat(rawParts, i);
for (int i = start; i < aliasParts.size(); i++) {
String aliasShortPkg = aliasParts.get(i);
JPackage pkgPart = new JPackage(rawPkg, aliasShortPkg);
JPackage pkgPart = new JPackage(concat(rawParts, i), aliasShortPkg);
JMenuItem pkgPartItem = new JMenuItem(aliasShortPkg);
pkgPartItem.addActionListener(e -> rename(pkgPart));
renameSubMenu.add(pkgPartItem);
......@@ -76,8 +77,9 @@ class JPackagePopupMenu extends JPopupMenu {
return sb.toString();
}
private void rename(JPackage pkgPart) {
new RenameDialog(mainWindow, pkgPart).setVisible(true);
private void rename(JPackage pkg) {
LOG.debug("Renaming package: fullName={}, name={}", pkg.getFullName(), pkg.getName());
new RenameDialog(mainWindow, pkg).setVisible(true);
}
private List<String> splitPackage(String rawPackage) {
......
......@@ -269,8 +269,12 @@ public class RenameDialog extends JDialog {
}
private void refreshJClass(JClass cls) {
cls.refresh();
IndexJob.refreshIndex(cache, cls.getCls());
try {
cls.reload();
IndexJob.refreshIndex(cache, cls.getCls());
} catch (Throwable e) {
LOG.error("Failed to reload class: {}", cls, e);
}
}
private void refreshTabs(TabbedPane tabbedPane, Set<JClass> updatedClasses) {
......
......@@ -29,7 +29,7 @@ public class DexClassData implements IClassData {
@Override
public IClassData copy() {
return new DexClassData(in.copy(), annotationsParser);
return new DexClassData(in.copy(), annotationsParser.copy());
}
@Override
......
......@@ -27,6 +27,10 @@ public class AnnotationsParser {
this.ext = ext;
}
public AnnotationsParser copy() {
return new AnnotationsParser(in.copy(), ext.copy());
}
public void setOffset(int offset) {
this.offset = offset;
if (offset == 0) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册