feat(gui): don't run full decompilation for usage search
New approach will run partial decompilation for classes from usage info collected on file load (pre-decompilation stage).
This commit is contained in:
@@ -201,9 +201,25 @@ public final class JavaClass implements JavaNode {
|
||||
|
||||
@Override
|
||||
public JavaClass getTopParentClass() {
|
||||
if (cls.contains(AFlag.ANONYMOUS_CLASS)) {
|
||||
// moved to usage class
|
||||
return getParentForAnonymousClass();
|
||||
}
|
||||
return parent == null ? this : parent.getTopParentClass();
|
||||
}
|
||||
|
||||
private JavaClass getParentForAnonymousClass() {
|
||||
List<JavaNode> useIn = getUseIn();
|
||||
if (useIn.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
JavaNode useNode = useIn.get(0);
|
||||
if (useNode.equals(this)) {
|
||||
return this;
|
||||
}
|
||||
return useNode.getTopParentClass();
|
||||
}
|
||||
|
||||
public AccessInfo getAccessInfo() {
|
||||
return cls.getAccessFlags();
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class DbgUtils {
|
||||
clsSig = DbgUtils.classSigToFullName(clsSig);
|
||||
JavaClass cls = mainWindow.getWrapper().getDecompiler().searchJavaClassOrItsParentByOrigFullName(clsSig);
|
||||
if (cls != null) {
|
||||
JClass jc = (JClass) mainWindow.getCacheObject().getNodeCache().makeFrom(cls);
|
||||
JClass jc = mainWindow.getCacheObject().getNodeCache().makeFrom(cls);
|
||||
return jc.getRootClass();
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -129,7 +129,7 @@ public class BackgroundExecutor {
|
||||
return cancelStatus;
|
||||
}
|
||||
setProgress(calcProgress(executor.getCompletedTaskCount()));
|
||||
Thread.sleep(500);
|
||||
Thread.sleep(300);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOG.debug("Task wait interrupted");
|
||||
|
||||
@@ -3,6 +3,7 @@ package jadx.gui.jobs;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
@@ -42,15 +43,18 @@ public class DecompileTask implements IBackgroundTask {
|
||||
|
||||
@Override
|
||||
public List<Runnable> scheduleJobs() {
|
||||
List<JavaClass> classes = wrapper.getIncludedClasses();
|
||||
expectedCompleteCount = classes.size();
|
||||
|
||||
IndexService indexService = mainWindow.getCacheObject().getIndexService();
|
||||
List<JavaClass> classesForIndex = wrapper.getIncludedClasses()
|
||||
.stream()
|
||||
.filter(indexService::isIndexNeeded)
|
||||
.collect(Collectors.toList());
|
||||
expectedCompleteCount = classesForIndex.size();
|
||||
|
||||
indexService.setComplete(false);
|
||||
complete.set(0);
|
||||
|
||||
List<Runnable> jobs = new ArrayList<>(expectedCompleteCount + 1);
|
||||
for (JavaClass cls : classes) {
|
||||
for (JavaClass cls : classesForIndex) {
|
||||
jobs.add(() -> {
|
||||
cls.decompile();
|
||||
indexService.indexCls(cls);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package jadx.gui.jobs;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
@@ -20,6 +22,7 @@ public class IndexService {
|
||||
|
||||
private final CacheObject cache;
|
||||
private boolean indexComplete;
|
||||
private final Set<JavaClass> indexSet = new HashSet<>();
|
||||
|
||||
public IndexService(CacheObject cache) {
|
||||
this.cache = cache;
|
||||
@@ -40,6 +43,7 @@ public class IndexService {
|
||||
|
||||
usageInfo.processClass(cls, linesInfo, lines);
|
||||
index.indexCode(cls, linesInfo, lines);
|
||||
indexSet.add(cls);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Index error in class: {}", cls.getFullName(), e);
|
||||
}
|
||||
@@ -56,11 +60,16 @@ public class IndexService {
|
||||
if (index == null || usageInfo == null) {
|
||||
return;
|
||||
}
|
||||
indexSet.remove(cls);
|
||||
index.remove(cls);
|
||||
usageInfo.remove(cls);
|
||||
indexCls(cls);
|
||||
}
|
||||
|
||||
public boolean isIndexNeeded(JavaClass cls) {
|
||||
return !indexSet.contains(cls);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static List<StringRef> splitLines(JavaClass cls) {
|
||||
List<StringRef> lines = StringRef.split(cls.getCode(), ICodeWriter.NL);
|
||||
|
||||
@@ -519,6 +519,9 @@ public class MainWindow extends JFrame {
|
||||
|
||||
public void waitDecompileTask() {
|
||||
synchronized (DECOMPILER_TASK_SYNC) {
|
||||
if (cacheObject.getIndexService().isComplete()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DecompileTask decompileTask = new DecompileTask(this, wrapper);
|
||||
Future<TaskStatus> task = backgroundExecutor.execute(decompileTask);
|
||||
|
||||
@@ -547,14 +547,14 @@ public abstract class CommonSearchDialog extends JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStartCommon() {
|
||||
void loadStartCommon() {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
progressPane.setIndeterminate(true);
|
||||
progressPane.setVisible(true);
|
||||
warnLabel.setVisible(false);
|
||||
}
|
||||
|
||||
private void loadFinishedCommon() {
|
||||
void loadFinishedCommon() {
|
||||
setCursor(null);
|
||||
progressPane.setVisible(false);
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ package jadx.gui.ui.dialog;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
import java.awt.FlowLayout;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
@@ -10,10 +13,15 @@ import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.WindowConstants;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.gui.jobs.IndexService;
|
||||
import jadx.gui.jobs.TaskStatus;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.CodeUsageInfo;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class UsageDialog extends CommonSearchDialog {
|
||||
|
||||
@@ -32,7 +40,40 @@ public class UsageDialog extends CommonSearchDialog {
|
||||
|
||||
@Override
|
||||
protected void openInit() {
|
||||
prepare();
|
||||
IndexService indexService = mainWindow.getCacheObject().getIndexService();
|
||||
if (indexService.isComplete()) {
|
||||
loadFinishedCommon();
|
||||
loadFinished();
|
||||
return;
|
||||
}
|
||||
List<JavaNode> useIn = node.getJavaNode().getUseIn();
|
||||
List<JavaClass> usageTopClsForIndex = useIn
|
||||
.stream()
|
||||
.map(JavaNode::getTopParentClass)
|
||||
.filter(indexService::isIndexNeeded)
|
||||
.distinct()
|
||||
.sorted(Comparator.comparing(JavaClass::getFullName))
|
||||
.collect(Collectors.toList());
|
||||
if (usageTopClsForIndex.isEmpty()) {
|
||||
loadFinishedCommon();
|
||||
loadFinished();
|
||||
return;
|
||||
}
|
||||
mainWindow.getBackgroundExecutor().execute(NLS.str("progress.load"),
|
||||
() -> {
|
||||
for (JavaClass cls : usageTopClsForIndex) {
|
||||
cls.decompile();
|
||||
indexService.indexCls(cls);
|
||||
}
|
||||
},
|
||||
(status) -> {
|
||||
if (status == TaskStatus.CANCEL_BY_MEMORY) {
|
||||
mainWindow.showHeapUsageBar();
|
||||
UiUtils.errorMessage(this, NLS.str("message.memoryLow"));
|
||||
}
|
||||
loadFinishedCommon();
|
||||
loadFinished();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,25 +31,29 @@ public class JNodeCache {
|
||||
return jNode;
|
||||
}
|
||||
|
||||
public JClass makeFrom(JavaClass javaCls) {
|
||||
if (javaCls == null) {
|
||||
return null;
|
||||
}
|
||||
return (JClass) cache.computeIfAbsent(javaCls,
|
||||
jn -> new JClass(javaCls, makeFrom(javaCls.getDeclaringClass())));
|
||||
}
|
||||
|
||||
private JNode convert(JavaNode node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
if (node instanceof JavaClass) {
|
||||
JClass p = (JClass) makeFrom(node.getDeclaringClass());
|
||||
return new JClass((JavaClass) node, p);
|
||||
return new JClass((JavaClass) node, makeFrom(node.getDeclaringClass()));
|
||||
}
|
||||
if (node instanceof JavaMethod) {
|
||||
JavaMethod mth = (JavaMethod) node;
|
||||
return new JMethod(mth, (JClass) makeFrom(mth.getDeclaringClass()));
|
||||
return new JMethod((JavaMethod) node, makeFrom(node.getDeclaringClass()));
|
||||
}
|
||||
if (node instanceof JavaField) {
|
||||
JavaField fld = (JavaField) node;
|
||||
return new JField(fld, (JClass) makeFrom(fld.getDeclaringClass()));
|
||||
return new JField((JavaField) node, makeFrom(node.getDeclaringClass()));
|
||||
}
|
||||
if (node instanceof JavaVariable) {
|
||||
JavaVariable var = (JavaVariable) node;
|
||||
return new JVariable(var, (JClass) makeFrom(var.getDeclaringClass()));
|
||||
return new JVariable((JavaVariable) node, makeFrom(node.getDeclaringClass()));
|
||||
}
|
||||
throw new JadxRuntimeException("Unknown type for JavaNode: " + node.getClass());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user