gui: More advanced implementation of renaming

This implementation does not reload file after renaming, and so works faster.
This commit is contained in:
S-trace
2019-12-11 10:38:15 +03:00
committed by Soul Trace
parent 02213802c5
commit 9dd5a9ef89
15 changed files with 169 additions and 29 deletions
@@ -8,4 +8,6 @@ public interface ICodeCache {
@Nullable
ICodeInfo get(String clsFullName);
void remove(String clsFullName);
}
@@ -57,6 +57,10 @@ public final class JavaClass implements JavaNode {
cls.decompile();
}
public void refresh() {
cls.refresh();
}
public synchronized String getSmali() {
return cls.getSmali();
}
@@ -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);
}
}
@@ -16,4 +16,8 @@ public class NoOpCodeCache implements ICodeCache {
public @Nullable ICodeInfo get(String clsFullName) {
return null;
}
@Override
public void remove(String clsFullName) {
}
}
@@ -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()) {
@@ -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) {
@@ -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();
@@ -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<String> 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<String> 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<String> readAndUpdateDeobfMap(Path deobfMapPath, String alias) throws IOException {
List<String> deobfMap = Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8);
@NotNull
private List<String> readDeobfMap(Path deobfMapPath) throws IOException {
return Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8);
}
private List<String> updateDeobfMap(List<String> 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<String> 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<JNode, ContentPanel> 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);
@@ -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) {
@@ -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);
@@ -70,4 +70,8 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel {
return javaCodePanel.getCodeArea();
}
public CodePanel getJavaCodePanel() {
return javaCodePanel;
}
}
@@ -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);
@@ -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);
});
}
}
@@ -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
@@ -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;