diff --git a/jadx-core/src/main/java/jadx/api/ICodeCache.java b/jadx-core/src/main/java/jadx/api/ICodeCache.java index a078f79cf..bc121067b 100644 --- a/jadx-core/src/main/java/jadx/api/ICodeCache.java +++ b/jadx-core/src/main/java/jadx/api/ICodeCache.java @@ -8,4 +8,6 @@ public interface ICodeCache { @Nullable ICodeInfo get(String clsFullName); + + void remove(String clsFullName); } diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 9909c50b0..67d4aba53 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -57,6 +57,10 @@ public final class JavaClass implements JavaNode { cls.decompile(); } + public void refresh() { + cls.refresh(); + } + public synchronized String getSmali() { return cls.getSmali(); } diff --git a/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java b/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java index 04d80fef8..8f36b4c0e 100644 --- a/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java +++ b/jadx-core/src/main/java/jadx/api/impl/InMemoryCodeCache.java @@ -21,4 +21,9 @@ public class InMemoryCodeCache implements ICodeCache { public @Nullable ICodeInfo get(String clsFullName) { return storage.get(clsFullName); } + + @Override + public void remove(String clsFullName) { + storage.remove(clsFullName); + } } diff --git a/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java b/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java index f764cde4e..cf738372c 100644 --- a/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java +++ b/jadx-core/src/main/java/jadx/api/impl/NoOpCodeCache.java @@ -16,4 +16,8 @@ public class NoOpCodeCache implements ICodeCache { public @Nullable ICodeInfo get(String clsFullName) { return null; } + + @Override + public void remove(String clsFullName) { + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index a73fb33b3..034f0dbec 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -289,6 +289,11 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { return codeInfo; } + public synchronized ICodeInfo refresh() { + load(); + return decompile(false); + } + @Override public void load() { for (MethodNode mth : getMethods()) { diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java index 46becf9ab..8f7aada3c 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java @@ -59,6 +59,13 @@ public class JClass extends JLoadableNode { update(); } + public synchronized void refresh() { + cls.refresh(); + loaded = true; + update(); + cls.unload(); + } + public synchronized void update() { removeAllChildren(); if (!loaded) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 503185d87..f28b6d7b6 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -396,6 +396,11 @@ public class MainWindow extends JFrame { cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount)); } + void resetIndex() { + int threadsCount = settings.getThreadsCount(); + cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount)); + } + private synchronized void runBackgroundJobs() { cancelBackgroundJobs(); backgroundWorker = new BackgroundWorker(cacheObject, progressPane); @@ -512,7 +517,7 @@ public class MainWindow extends JFrame { treeModel.reload(); } - private void reloadTree() { + void reloadTree() { treeReloading = true; treeModel.reload(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java index b70f87704..f21b98a0e 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java @@ -10,6 +10,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.List; +import java.util.Map; import javax.swing.*; @@ -23,13 +24,18 @@ import jadx.api.JavaMethod; import jadx.api.JavaNode; import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.visitors.RenameVisitor; import jadx.core.utils.files.InputFile; +import jadx.gui.treemodel.CodeNode; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JField; import jadx.gui.treemodel.JMethod; import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JPackage; +import jadx.gui.ui.codearea.ClassCodeContentPanel; import jadx.gui.ui.codearea.CodeArea; +import jadx.gui.ui.codearea.CodePanel; +import jadx.gui.utils.CodeUsageInfo; import jadx.gui.utils.NLS; import jadx.gui.utils.TextStandardActions; @@ -105,42 +111,37 @@ public class RenameDialog extends JDialog { return String.format("%s %s = %s", type, id, renameText); } - private boolean updateDeobfMap(String renameText, RootNode root) { - Path deobfMapPath = getDeobfMapPath(root); + private boolean writeDeobfMapFile(Path deobfMapPath, List deobfMap) throws IOException { if (deobfMapPath == null) { - LOG.error("rename(): Failed deofbMapFile is null"); + LOG.error("updateDeobfMapFile(): deobfMapPath is null!"); return false; } - String alias = getNodeAlias(renameText); - LOG.info("rename(): {}", alias); - try { - List deobfMap = readAndUpdateDeobfMap(deobfMapPath, alias); - File tmpFile = File.createTempFile("deobf_tmp_", ".txt"); - try (FileOutputStream fileOut = new FileOutputStream(tmpFile)) { - for (String entry : deobfMap) { - fileOut.write(entry.getBytes()); - fileOut.write(System.lineSeparator().getBytes()); - } - } - File oldMap = File.createTempFile("deobf_bak_", ".txt"); - Files.copy(deobfMapPath, oldMap.toPath(), StandardCopyOption.REPLACE_EXISTING); - Files.copy(tmpFile.toPath(), deobfMapPath, StandardCopyOption.REPLACE_EXISTING); - Files.delete(oldMap.toPath()); - } catch (Exception e) { - LOG.error("rename(): Failed to write deofbMapFile {}", deobfMapPath, e); - return false; + + File tmpFile = File.createTempFile("deobf_tmp_", ".txt"); + FileOutputStream fileOut = new FileOutputStream(tmpFile); + for (String entry : deobfMap) { + fileOut.write(entry.getBytes()); + fileOut.write(System.lineSeparator().getBytes()); } + fileOut.close(); + File oldMap = File.createTempFile("deobf_bak_", ".txt"); + Files.copy(deobfMapPath, oldMap.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(tmpFile.toPath(), deobfMapPath, StandardCopyOption.REPLACE_EXISTING); + Files.delete(oldMap.toPath()); return true; } - private List readAndUpdateDeobfMap(Path deobfMapPath, String alias) throws IOException { - List deobfMap = Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8); + @NotNull + private List readDeobfMap(Path deobfMapPath) throws IOException { + return Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8); + } + + private List updateDeobfMap(List deobfMap, String alias) { String id = alias.split("=")[0]; - LOG.info("Id = {}", id); int i = 0; while (i < deobfMap.size()) { if (deobfMap.get(i).startsWith(id)) { - LOG.info("Removing entry {}", deobfMap.get(i)); + LOG.info("updateDeobfMap(): Removing entry " + deobfMap.get(i)); deobfMap.remove(i); } else { i++; @@ -161,15 +162,79 @@ public class RenameDialog extends JDialog { dispose(); return; } - if (!updateDeobfMap(renameText, root)) { - LOG.error("rename(): updateDeobfMap() failed"); + if (!refreshDeobfMapFile(renameText, root)) { dispose(); return; } - mainWindow.reOpenFile(); + refreshState(root); dispose(); } + private boolean refreshDeobfMapFile(String renameText, RootNode root) { + List deobfMap; + Path deobfMapPath = getDeobfMapPath(root); + try { + deobfMap = readDeobfMap(deobfMapPath); + } catch (IOException e) { + LOG.error("rename(): readDeobfMap() failed"); + dispose(); + return false; + } + updateDeobfMap(deobfMap, getNodeAlias(renameText)); + try { + writeDeobfMapFile(deobfMapPath, deobfMap); + } catch (IOException e) { + LOG.error("rename(): writeDeobfMap() failed"); + dispose(); + return false; + } + try { + writeDeobfMapFile(deobfMapPath, deobfMap); + } catch (IOException e) { + LOG.error("rename(): updateDeobfMap() failed"); + dispose(); + return false; + } + return true; + } + + private void refreshState(RootNode rootNode) { + RenameVisitor renameVisitor = new RenameVisitor(); + renameVisitor.init(rootNode); + + mainWindow.getCacheObject().getNodeCache().refresh(node); + + TabbedPane tabbedPane = mainWindow.getTabbedPane(); + refreshTabs(tabbedPane); + refreshUsages(); + mainWindow.resetIndex(); + mainWindow.reloadTree(); + } + + private void refreshTabs(TabbedPane tabbedPane) { + for (Map.Entry panel : tabbedPane.getOpenTabs().entrySet()) { + ContentPanel contentPanel = panel.getValue(); + if (contentPanel instanceof ClassCodeContentPanel) { + JNode node = panel.getKey(); + node.getRootClass().refresh(); // Update code cache + ClassCodeContentPanel codePanel = (ClassCodeContentPanel) contentPanel; + CodePanel javaPanel = codePanel.getJavaCodePanel(); + javaPanel.refresh(); + tabbedPane.refresh(node); + } + } + } + + private void refreshUsages() { + CodeUsageInfo usageInfo = mainWindow.getCacheObject().getUsageInfo(); + if (usageInfo == null) { + return; + } + for (CodeNode node : usageInfo.getUsageList(node)) { + node.getRootClass().refresh(); // Update code cache + } + } + private void initCommon() { KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); getRootPane().registerKeyboardAction(e -> dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java b/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java index ae0627b10..72ff21eb7 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java @@ -155,6 +155,13 @@ public class TabbedPane extends JTabbedPane { return panel; } + public void refresh(JNode node) { + ContentPanel panel = openTabs.get(node); + if (panel != null) { + setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel)); + } + } + @Nullable private ContentPanel makeContentPanel(JNode node) { if (node instanceof JResource) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java index 90068eaa8..308f8f633 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java @@ -53,6 +53,12 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea { */ public abstract void load(); + /** + * Implement in this method the code that reloads node from cache and sets the new content to be + * displayed + */ + public abstract void refresh(); + public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) { RSyntaxTextArea area = new RSyntaxTextArea(); area.setEditable(false); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java index ff9b1ebde..937795ef5 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/ClassCodeContentPanel.java @@ -70,4 +70,8 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel { return javaCodePanel.getCodeArea(); } + public CodePanel getJavaCodePanel() { + return javaCodePanel; + } + } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java index 8e75db620..29364e256 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java @@ -51,6 +51,11 @@ public final class CodeArea extends AbstractCodeArea { } } + @Override + public void refresh() { + setText(node.getContent()); + } + private void addMenuItems() { FindUsageAction findUsage = new FindUsageAction(this); GoToDeclarationAction goToDeclaration = new GoToDeclarationAction(this); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodePanel.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodePanel.java index f715092f3..45c97831a 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodePanel.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodePanel.java @@ -87,4 +87,14 @@ public class CodePanel extends JPanel { public JScrollPane getCodeScrollPane() { return codeScrollPane; } + + public void refresh() { + JViewport viewport = getCodeScrollPane().getViewport(); + Point viewPosition = viewport.getViewPosition(); + codeArea.refresh(); + initLineNumbers(); + SwingUtilities.invokeLater(() -> { + viewport.setViewPosition(viewPosition); + }); + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java index 41200a3bf..c2bacad60 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java @@ -25,6 +25,11 @@ public final class SmaliArea extends AbstractCodeArea { } } + @Override + public void refresh() { + load(); + } + @Override public JNode getNode() { // this area contains only smali without other node attributes diff --git a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java index 4d46e2b73..dee949204 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java @@ -29,6 +29,12 @@ public class JNodeCache { return jNode; } + public void refresh(JNode node) { + JavaNode javaNode = node.getJavaNode(); + cache.remove(javaNode); + makeFrom(javaNode); + } + private JNode convert(JavaNode node) { if (node == null) { return null;