未验证 提交 1df217c4 编写于 作者: S Skylot

fix: save cache dir for reuse on project save/reopen

上级 81f209ba
package jadx.gui;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
......@@ -24,6 +23,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.codecache.CodeStringCache;
import jadx.gui.utils.codecache.disk.BufferCodeCache;
import jadx.gui.utils.codecache.disk.DiskCodeCache;
......@@ -35,26 +35,22 @@ import static jadx.core.dex.nodes.ProcessState.PROCESS_COMPLETE;
public class JadxWrapper {
private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class);
private final JadxSettings settings;
private final MainWindow mainWindow;
private JadxDecompiler decompiler;
private @Nullable JadxProject project;
private List<Path> openPaths = Collections.emptyList();
public JadxWrapper(JadxSettings settings) {
this.settings = settings;
this.decompiler = new JadxDecompiler(settings.toJadxArgs());
public JadxWrapper(MainWindow mainWindow) {
this.mainWindow = mainWindow;
this.decompiler = new JadxDecompiler(new JadxArgs());
}
public void openFile(List<Path> paths) {
public void open() {
close();
this.openPaths = paths;
try {
JadxArgs jadxArgs = settings.toJadxArgs();
jadxArgs.setInputFiles(FileUtils.toFiles(paths));
if (project != null) {
jadxArgs.setCodeData(project.getCodeData());
}
closeCodeCache();
JadxProject project = getProject();
JadxArgs jadxArgs = getSettings().toJadxArgs();
jadxArgs.setInputFiles(FileUtils.toFiles(project.getFilePaths()));
jadxArgs.setCodeData(project.getCodeData());
this.decompiler = new JadxDecompiler(jadxArgs);
this.decompiler.load();
initCodeCache(jadxArgs);
......@@ -80,11 +76,10 @@ public class JadxWrapper {
} catch (Exception e) {
LOG.error("jadx decompiler close error", e);
}
this.openPaths = Collections.emptyList();
}
private void initCodeCache(JadxArgs jadxArgs) {
switch (settings.getCodeCacheMode()) {
switch (getSettings().getCodeCacheMode()) {
case MEMORY:
jadxArgs.setCodeCache(new InMemoryCodeCache());
break;
......@@ -98,22 +93,10 @@ public class JadxWrapper {
}
private BufferCodeCache buildBufferedDiskCache() {
DiskCodeCache diskCache = new DiskCodeCache(decompiler.getRoot(), getCacheDir());
DiskCodeCache diskCache = new DiskCodeCache(decompiler.getRoot(), getProject().getCacheDir());
return new BufferCodeCache(diskCache);
}
private Path getCacheDir() {
if (project != null && project.getProjectPath() != null) {
Path projectPath = project.getProjectPath();
return projectPath.resolveSibling(projectPath.getFileName() + ".cache");
}
if (!openPaths.isEmpty()) {
Path path = openPaths.get(0);
return path.resolveSibling(path.getFileName() + ".cache");
}
throw new JadxRuntimeException("Can't get working dir");
}
public void closeCodeCache() {
ICodeCache codeCache = getArgs().getCodeCache();
if (codeCache != null) {
......@@ -177,29 +160,29 @@ public class JadxWrapper {
// TODO: move to CLI and filter classes in JadxDecompiler
public List<String> getExcludedPackages() {
String excludedPackages = settings.getExcludedPackages().trim();
String excludedPackages = getSettings().getExcludedPackages().trim();
if (excludedPackages.isEmpty()) {
return Collections.emptyList();
}
return Arrays.asList(excludedPackages.split("[ ]+"));
return Arrays.asList(excludedPackages.split(" +"));
}
public void setExcludedPackages(List<String> packagesToExclude) {
settings.setExcludedPackages(String.join(" ", packagesToExclude).trim());
settings.sync();
getSettings().setExcludedPackages(String.join(" ", packagesToExclude).trim());
getSettings().sync();
}
public void addExcludedPackage(String packageToExclude) {
String newExclusion = settings.getExcludedPackages() + ' ' + packageToExclude;
settings.setExcludedPackages(newExclusion.trim());
settings.sync();
String newExclusion = getSettings().getExcludedPackages() + ' ' + packageToExclude;
getSettings().setExcludedPackages(newExclusion.trim());
getSettings().sync();
}
public void removeExcludedPackage(String packageToRemoveFromExclusion) {
List<String> list = new ArrayList<>(getExcludedPackages());
list.remove(packageToRemoveFromExclusion);
settings.setExcludedPackages(String.join(" ", list));
settings.sync();
getSettings().setExcludedPackages(String.join(" ", list));
getSettings().sync();
}
public List<JavaPackage> getPackages() {
......@@ -210,10 +193,6 @@ public class JadxWrapper {
return decompiler.getResources();
}
public List<Path> getOpenPaths() {
return openPaths;
}
public JadxDecompiler getDecompiler() {
return decompiler;
}
......@@ -222,8 +201,12 @@ public class JadxWrapper {
return decompiler.getArgs();
}
public void setProject(JadxProject project) {
this.project = project;
public JadxProject getProject() {
return mainWindow.getProject();
}
public JadxSettings getSettings() {
return mainWindow.getSettings();
}
/**
......
......@@ -39,7 +39,7 @@ public class QuarkDialog extends JDialog {
this.files = filterOpenFiles(mainWindow);
if (files.isEmpty()) {
UiUtils.errorMessage(mainWindow, "Quark is unable to analyze loaded files");
LOG.error("Quark: The files cannot be analyzed: {}", mainWindow.getWrapper().getOpenPaths());
LOG.error("Quark: The files cannot be analyzed: {}", mainWindow.getProject().getFilePaths());
return;
}
initUI();
......@@ -47,7 +47,7 @@ public class QuarkDialog extends JDialog {
private List<Path> filterOpenFiles(MainWindow mainWindow) {
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.{apk,dex}");
return mainWindow.getWrapper().getOpenPaths()
return mainWindow.getProject().getFilePaths()
.stream()
.filter(matcher::matches)
.collect(Collectors.toList());
......
......@@ -11,6 +11,8 @@ import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -41,30 +43,26 @@ public class JadxProject {
private static final int CURRENT_PROJECT_VERSION = 1;
public static final String PROJECT_EXTENSION = "jadx";
private transient MainWindow mainWindow;
private transient JadxSettings settings;
private final transient MainWindow mainWindow;
private transient String name = "New Project";
private transient Path projectPath;
private transient @Nullable Path projectPath;
private transient boolean initial = true;
private transient boolean saved;
private ProjectData data = new ProjectData();
public void setSettings(JadxSettings settings) {
this.settings = settings;
}
public void setMainWindow(MainWindow mainWindow) {
public JadxProject(MainWindow mainWindow) {
this.mainWindow = mainWindow;
}
@Nullable
public Path getProjectPath() {
return projectPath;
}
private void setProjectPath(Path projectPath) {
private void setProjectPath(@NotNull Path projectPath) {
this.projectPath = projectPath;
this.name = CommonFileUtils.removeFileExtension(projectPath.getFileName().toString());
changed();
......@@ -74,7 +72,7 @@ public class JadxProject {
return data.getFiles();
}
public void setFilePath(List<Path> files) {
public void setFilePaths(List<Path> files) {
if (!files.equals(getFilePaths())) {
data.setFiles(files);
String joinedName = files.stream().map(p -> CommonFileUtils.removeFileExtension(p.getFileName().toString()))
......@@ -144,22 +142,52 @@ public class JadxProject {
return data.getActiveTab();
}
public @NotNull Path getCacheDir() {
Path cacheDir = data.getCacheDir();
if (cacheDir != null) {
return cacheDir;
}
Path newCacheDir = buildCacheDir();
setCacheDir(newCacheDir);
return newCacheDir;
}
public void setCacheDir(Path cacheDir) {
data.setCacheDir(cacheDir);
changed();
}
private Path buildCacheDir() {
if (projectPath != null) {
return projectPath.resolveSibling(projectPath.getFileName() + ".cache");
}
List<Path> files = data.getFiles();
if (!files.isEmpty()) {
Path path = files.get(0);
return path.resolveSibling(path.getFileName() + ".cache");
}
throw new JadxRuntimeException("Can't get working dir");
}
private void changed() {
JadxSettings settings = mainWindow.getSettings();
if (settings != null && settings.isAutoSaveProject()) {
save();
} else {
saved = false;
}
initial = false;
if (mainWindow != null) {
mainWindow.updateProject(this);
}
mainWindow.updateProject(this);
}
public String getName() {
return name;
}
public boolean isSaveFileSelected() {
return projectPath != null;
}
public boolean isSaved() {
return saved;
}
......@@ -186,10 +214,10 @@ public class JadxProject {
}
}
public static JadxProject from(Path path) {
public static JadxProject load(MainWindow mainWindow, Path path) {
Path basePath = path.toAbsolutePath().getParent();
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
JadxProject project = new JadxProject();
JadxProject project = new JadxProject(mainWindow);
project.data = buildGson(basePath).fromJson(reader, ProjectData.class);
project.saved = true;
project.setProjectPath(path);
......
......@@ -138,7 +138,7 @@ public class JadxSettingsWindow extends JDialog {
SwingUtilities.invokeLater(() -> {
if (needReload) {
mainWindow.reOpenFile();
mainWindow.reopen();
}
if (!settings.getLangLocale().equals(prevLang)) {
JOptionPane.showMessageDialog(
......
......@@ -6,6 +6,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import jadx.api.data.impl.JadxCodeData;
public class ProjectData {
......@@ -16,6 +18,7 @@ public class ProjectData {
private JadxCodeData codeData = new JadxCodeData();
private List<TabViewState> openTabs = Collections.emptyList();
private int activeTab = -1;
private @Nullable Path cacheDir;
public List<Path> getFiles() {
return files;
......@@ -82,4 +85,13 @@ public class ProjectData {
this.activeTab = activeTab;
return true;
}
@Nullable
public Path getCacheDir() {
return cacheDir;
}
public void setCacheDir(Path cacheDir) {
this.cacheDir = cacheDir;
}
}
......@@ -133,7 +133,7 @@ public class JRoot extends JNode {
@Override
public String makeString() {
List<Path> paths = wrapper.getOpenPaths();
List<Path> paths = wrapper.getProject().getFilePaths();
int count = paths.size();
if (count == 0) {
return "File not open";
......@@ -146,7 +146,7 @@ public class JRoot extends JNode {
@Override
public String getTooltip() {
List<Path> paths = wrapper.getOpenPaths();
List<Path> paths = wrapper.getProject().getFilePaths();
int count = paths.size();
if (count < 2) {
return null;
......
......@@ -85,7 +85,6 @@ import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.Jadx;
import jadx.core.utils.ListUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
import jadx.gui.JadxWrapper;
import jadx.gui.device.debugger.BreakpointManager;
......@@ -170,8 +169,9 @@ public class MainWindow extends JFrame {
private final transient JadxSettings settings;
private final transient CacheObject cacheObject;
private final transient BackgroundExecutor backgroundExecutor;
@NotNull
private transient JadxProject project = new JadxProject();
private transient @NotNull JadxProject project;
private transient Action newProjectAction;
private transient Action saveProjectAction;
......@@ -202,7 +202,8 @@ public class MainWindow extends JFrame {
public MainWindow(JadxSettings settings) {
this.settings = settings;
this.cacheObject = new CacheObject();
this.wrapper = new JadxWrapper(settings);
this.project = new JadxProject(this);
this.wrapper = new JadxWrapper(this);
resetCache();
FontUtils.registerBundledFonts();
......@@ -211,11 +212,11 @@ public class MainWindow extends JFrame {
registerMouseNavigationButtons();
UiUtils.setWindowIcons(this);
loadSettings();
update();
this.backgroundExecutor = new BackgroundExecutor(this);
checkForUpdate();
newProject();
}
public void init() {
......@@ -275,6 +276,10 @@ public class MainWindow extends JFrame {
}
public void openFileOrProject() {
saveAll();
if (!ensureProjectIsSaved()) {
return;
}
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.OPEN);
List<Path> openPaths = fileDialog.show();
if (!openPaths.isEmpty()) {
......@@ -287,20 +292,25 @@ public class MainWindow extends JFrame {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.ADD);
List<Path> addPaths = fileDialog.show();
if (!addPaths.isEmpty()) {
open(ListUtils.distinctMergeSortedLists(addPaths, wrapper.getOpenPaths()));
addFiles(addPaths);
}
}
public void addFiles(List<Path> addPaths) {
project.setFilePaths(ListUtils.distinctMergeSortedLists(addPaths, project.getFilePaths()));
reopen();
}
private void newProject() {
if (!ensureProjectIsSaved()) {
return;
}
closeAll();
updateProject(new JadxProject());
updateProject(new JadxProject(this));
}
private void saveProject() {
if (project.getProjectPath() == null) {
if (!project.isSaveFileSelected()) {
saveProjectAs();
} else {
project.save();
......@@ -312,9 +322,8 @@ public class MainWindow extends JFrame {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.SAVE_PROJECT);
if (project.getFilePaths().size() == 1) {
// If there is only one file loaded we suggest saving the jadx project file next to the loaded file
Path loadedFile = this.project.getFilePaths().get(0);
String fileName = loadedFile.getFileName() + "." + JadxProject.PROJECT_EXTENSION;
fileDialog.setSelectedFile(loadedFile.resolveSibling(fileName));
Path projectPath = getProjectPathForFile(this.project.getFilePaths().get(0));
fileDialog.setSelectedFile(projectPath);
}
List<Path> saveFiles = fileDialog.show();
if (saveFiles.isEmpty()) {
......@@ -344,29 +353,64 @@ public class MainWindow extends JFrame {
open(paths, EMPTY_RUNNABLE);
}
void open(List<Path> paths, Runnable onFinish) {
private void open(List<Path> paths, Runnable onFinish) {
saveAll();
closeAll();
if (paths.size() == 1) {
Path singleFile = paths.get(0);
String fileExtension = CommonFileUtils.getFileExtension(singleFile.getFileName().toString());
if (fileExtension != null && fileExtension.equalsIgnoreCase(JadxProject.PROJECT_EXTENSION)) {
List<Path> projectFiles = openProject(singleFile);
if (!Utils.isEmpty(projectFiles)) {
openFiles(projectFiles, onFinish);
}
return;
}
if (paths.size() == 1 && openSingleFile(paths.get(0), onFinish)) {
return;
}
openFiles(paths, onFinish);
// start new project
project = new JadxProject(this);
project.setFilePaths(paths);
loadFiles(onFinish);
}
private void openFiles(List<Path> paths, Runnable onFinish) {
project.setFilePath(paths);
if (paths.isEmpty()) {
private boolean openSingleFile(Path singleFile, Runnable onFinish) {
String fileExtension = CommonFileUtils.getFileExtension(singleFile.getFileName().toString());
if (fileExtension != null && fileExtension.equalsIgnoreCase(JadxProject.PROJECT_EXTENSION)) {
openProject(singleFile, onFinish);
return true;
}
// check if project file already saved with default name
Path projectPath = getProjectPathForFile(singleFile);
if (Files.exists(projectPath)) {
LOG.info("Loading project for this file");
openProject(projectPath, onFinish);
return true;
}
return false;
}
private static Path getProjectPathForFile(Path loadedFile) {
String fileName = loadedFile.getFileName() + "." + JadxProject.PROJECT_EXTENSION;
return loadedFile.resolveSibling(fileName);
}
public void reopen() {
loadFiles(EMPTY_RUNNABLE);
}
private void openProject(Path path, Runnable onFinish) {
JadxProject jadxProject = JadxProject.load(this, path);
if (jadxProject == null) {
JOptionPane.showMessageDialog(
this,
NLS.str("msg.project_error"),
NLS.str("msg.project_error_title"),
JOptionPane.INFORMATION_MESSAGE);
jadxProject = new JadxProject(this);
}
settings.addRecentProject(path);
project = jadxProject;
loadFiles(onFinish);
}
private void loadFiles(Runnable onFinish) {
if (project.getFilePaths().isEmpty()) {
return;
}
backgroundExecutor.execute(NLS.str("progress.load"),
() -> wrapper.openFile(paths),
wrapper::open,
status -> {
if (status == TaskStatus.CANCEL_BY_MEMORY) {
showHeapUsageBar();
......@@ -374,16 +418,19 @@ public class MainWindow extends JFrame {
return;
}
checkLoadedStatus();
onOpen(paths);
onOpen();
onFinish.run();
});
}
private void saveAll() {
saveOpenTabs();
BreakpointManager.saveAndExit();
}
private void closeAll() {
cancelBackgroundJobs();
saveOpenTabs();
clearTree();
BreakpointManager.saveAndExit();
LogCollector.getInstance().reset();
wrapper.close();
tabbedPane.closeAllTabs();
......@@ -409,11 +456,11 @@ public class MainWindow extends JFrame {
}
}
private void onOpen(List<Path> paths) {
private void onOpen() {
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
initTree();
update();
BreakpointManager.init(paths.get(0).toAbsolutePath().getParent());
BreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent());
backgroundExecutor.execute(NLS.str("progress.load"),
this::restoreOpenTabs,
......@@ -426,7 +473,7 @@ public class MainWindow extends JFrame {
}
private boolean ensureProjectIsSaved() {
if (project != null && !project.isSaved() && !project.isInitial()) {
if (!project.isSaved() && !project.isInitial()) {
int res = JOptionPane.showConfirmDialog(
this,
NLS.str("confirm.not_saved_message"),
......@@ -442,29 +489,8 @@ public class MainWindow extends JFrame {
return true;
}
private List<Path> openProject(Path path) {
if (!ensureProjectIsSaved()) {
return Collections.emptyList();
}
JadxProject jadxProject = JadxProject.from(path);
if (jadxProject == null) {
JOptionPane.showMessageDialog(
this,
NLS.str("msg.project_error"),
NLS.str("msg.project_error_title"),
JOptionPane.INFORMATION_MESSAGE);
jadxProject = new JadxProject();
}
updateProject(jadxProject);
settings.addRecentProject(path);
return jadxProject.getFilePaths();
}
public void updateProject(@NotNull JadxProject jadxProject) {
jadxProject.setSettings(settings);
jadxProject.setMainWindow(this);
this.project = jadxProject;
this.wrapper.setProject(jadxProject);
update();
}
......@@ -548,14 +574,6 @@ public class MainWindow extends JFrame {
backgroundExecutor.cancelAll();
}
public void reOpenFile() {
List<Path> openedFile = wrapper.getOpenPaths();
if (openedFile != null) {
saveOpenTabs();
open(openedFile);
}
}
private void saveAll(boolean export) {
FileDialog fileDialog = new FileDialog(this, FileDialog.OpenMode.EXPORT);
List<Path> saveDirs = fileDialog.show();
......@@ -651,7 +669,7 @@ public class MainWindow extends JFrame {
deobfToggleBtn.setSelected(deobfOn);
deobfMenuItem.setState(deobfOn);
reOpenFile();
reopen();
}
private boolean nodeClickAction(@Nullable Object obj) {
......@@ -1332,9 +1350,7 @@ public class MainWindow extends JFrame {
}
private void saveOpenTabs() {
if (project != null) {
project.saveOpenTabs(tabbedPane.getEditorViewStates(), tabbedPane.getSelectedIndex());
}
project.saveOpenTabs(tabbedPane.getEditorViewStates(), tabbedPane.getSelectedIndex());
}
private void restoreOpenTabs() {
......@@ -1432,7 +1448,7 @@ public class MainWindow extends JFrame {
@Override
public void menuSelected(MenuEvent menuEvent) {
Set<Path> current = new HashSet<>(wrapper.getOpenPaths());
Set<Path> current = new HashSet<>(project.getFilePaths());
List<JMenuItem> items = settings.getRecentProjects()
.stream()
.filter(path -> !current.contains(path))
......
......@@ -103,7 +103,7 @@ public class ExcludePkgDialog extends JDialog {
btnOk.addActionListener(e -> {
mainWindow.getWrapper().setExcludedPackages(getExcludes());
mainWindow.reOpenFile();
mainWindow.reopen();
dispose();
});
btnAll.addActionListener(e -> {
......
......@@ -120,7 +120,7 @@ public class JPackagePopupMenu extends JPopupMenu {
} else {
wrapper.removeExcludedPackage(fullName);
}
mainWindow.reOpenFile();
mainWindow.reopen();
});
return excludeItem;
}
......
......@@ -111,7 +111,8 @@ public class DiskCodeCache implements ICodeCache {
return FileVisitResult.CONTINUE;
}
});
LOG.info("Found {} classes in disk cache in {} ms", cachedKeys.size(), System.currentTimeMillis() - start);
LOG.info("Found {} classes metadata in disk cache in {} ms, dir: {}", cachedKeys.size(),
System.currentTimeMillis() - start, metaDir);
} catch (Exception e) {
LOG.error("Failed to collect cached items", e);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册