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