fix: use internal usage info for rename, fix index refresh (#791)
This commit is contained in:
@@ -6,6 +6,8 @@ public interface ICodeCache {
|
||||
|
||||
void add(String clsFullName, ICodeInfo codeInfo);
|
||||
|
||||
void remove(String clsFullName);
|
||||
|
||||
@Nullable
|
||||
ICodeInfo get(String clsFullName);
|
||||
}
|
||||
|
||||
@@ -4,16 +4,19 @@ import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@@ -422,6 +425,13 @@ public final class JadxDecompiler implements Closeable {
|
||||
throw new JadxRuntimeException("Unexpected node type: " + obj);
|
||||
}
|
||||
|
||||
List<JavaNode> convertNodes(Collection<?> nodesList) {
|
||||
return nodesList.stream()
|
||||
.map(this::convertNode)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JavaNode getJavaNodeAtPosition(ICodeInfo codeInfo, int line, int offset) {
|
||||
Map<CodePosition, Object> map = codeInfo.getAnnotations();
|
||||
|
||||
@@ -59,6 +59,8 @@ public final class JavaClass implements JavaNode {
|
||||
|
||||
public synchronized void refresh() {
|
||||
listsLoaded = false;
|
||||
cls.unload();
|
||||
cls.deepUnload();
|
||||
cls.reRunDecompile();
|
||||
}
|
||||
|
||||
@@ -124,7 +126,7 @@ public final class JavaClass implements JavaNode {
|
||||
}
|
||||
}
|
||||
|
||||
private JadxDecompiler getRootDecompiler() {
|
||||
protected JadxDecompiler getRootDecompiler() {
|
||||
if (parent != null) {
|
||||
return parent.getRootDecompiler();
|
||||
}
|
||||
@@ -156,6 +158,11 @@ public final class JavaClass implements JavaNode {
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JavaNode> getUseIn() {
|
||||
return getRootDecompiler().convertNodes(cls.getUseIn());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public JavaNode getJavaNodeAtPosition(int line, int offset) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
@@ -47,6 +49,11 @@ public final class JavaField implements JavaNode {
|
||||
return field.getDecompiledLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JavaNode> getUseIn() {
|
||||
return getDeclaringClass().getRootDecompiler().convertNodes(field.getUseIn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal API. Not Stable!
|
||||
*/
|
||||
|
||||
@@ -56,6 +56,11 @@ public final class JavaMethod implements JavaNode {
|
||||
return ArgType.tryToResolveClassAlias(mth.root(), retType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JavaNode> getUseIn() {
|
||||
return getDeclaringClass().getRootDecompiler().convertNodes(mth.getUseIn());
|
||||
}
|
||||
|
||||
public boolean isConstructor() {
|
||||
return mth.getMethodInfo().isConstructor();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface JavaNode {
|
||||
|
||||
String getName();
|
||||
@@ -11,4 +13,6 @@ public interface JavaNode {
|
||||
JavaClass getTopParentClass();
|
||||
|
||||
int getDecompiledLine();
|
||||
|
||||
List<JavaNode> getUseIn();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -43,6 +44,11 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JavaNode> getUseIn() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull JavaPackage o) {
|
||||
return name.compareTo(o.name);
|
||||
|
||||
@@ -17,6 +17,11 @@ public class InMemoryCodeCache implements ICodeCache {
|
||||
storage.put(clsFullName, codeInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String clsFullName) {
|
||||
storage.remove(clsFullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ICodeInfo get(String clsFullName) {
|
||||
return storage.get(clsFullName);
|
||||
|
||||
@@ -12,6 +12,11 @@ public class NoOpCodeCache implements ICodeCache {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String clsFullName) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ICodeInfo get(String clsFullName) {
|
||||
return null;
|
||||
|
||||
@@ -699,12 +699,12 @@ public class ClassGen {
|
||||
List<ClassNode> deps = cls.getDependencies();
|
||||
code.startLine("// deps - ").add(Integer.toString(deps.size()));
|
||||
for (ClassNode depCls : deps) {
|
||||
code.startLine("// ").add(depCls.getFullName());
|
||||
code.startLine("// ").add(depCls.getClassInfo().getFullName());
|
||||
}
|
||||
List<ClassNode> useIn = cls.getUseIn();
|
||||
code.startLine("// use in - ").add(Integer.toString(useIn.size()));
|
||||
for (ClassNode useCls : useIn) {
|
||||
code.startLine("// ").add(useCls.getFullName());
|
||||
code.startLine("// ").add(useCls.getClassInfo().getFullName());
|
||||
}
|
||||
List<MethodNode> useInMths = cls.getUseInMth();
|
||||
code.startLine("// use in methods - ").add(Integer.toString(useInMths.size()));
|
||||
|
||||
@@ -345,6 +345,9 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
if (state == NOT_LOADED) {
|
||||
return;
|
||||
}
|
||||
methods.forEach(MethodNode::unload);
|
||||
innerClasses.forEach(ClassNode::unload);
|
||||
fields.forEach(FieldNode::unloadAttributes);
|
||||
|
||||
@@ -3,7 +3,7 @@ package jadx.core.dex.nodes;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
|
||||
public interface ICodeNode extends IDexNode, IAttributeNode {
|
||||
public interface ICodeNode extends IDexNode, IAttributeNode, IUsageInfoNode {
|
||||
AccessInfo getAccessFlags();
|
||||
|
||||
void setAccessFlags(AccessInfo newAccessFlags);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IUsageInfoNode {
|
||||
List<? extends ICodeNode> getUseIn();
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.api.plugins.input.data.ICodeReader;
|
||||
import jadx.api.plugins.input.insns.InsnData;
|
||||
import jadx.api.plugins.input.insns.Opcode;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
@@ -49,9 +48,6 @@ public class UsageInfoVisitor extends AbstractVisitor {
|
||||
}
|
||||
// TODO: process annotations and generics
|
||||
for (MethodNode methodNode : cls.getMethods()) {
|
||||
if (methodNode.isNoCode() || methodNode.contains(AType.JADX_ERROR)) {
|
||||
continue;
|
||||
}
|
||||
processMethod(methodNode, usageInfo);
|
||||
}
|
||||
}
|
||||
@@ -70,6 +66,9 @@ public class UsageInfoVisitor extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private static void processInstructions(MethodNode mth, UsageInfo usageInfo) {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
ICodeReader codeReader = mth.getCodeReader();
|
||||
if (codeReader == null) {
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.*;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@@ -36,8 +36,10 @@ public class BackgroundExecutor {
|
||||
|
||||
public Future<Boolean> execute(IBackgroundTask task) {
|
||||
TaskWorker taskWorker = new TaskWorker(task);
|
||||
taskWorker.init();
|
||||
taskQueueExecutor.execute(taskWorker);
|
||||
taskQueueExecutor.execute(() -> {
|
||||
taskWorker.init();
|
||||
taskWorker.run();
|
||||
});
|
||||
return taskWorker;
|
||||
}
|
||||
|
||||
@@ -52,6 +54,14 @@ public class BackgroundExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(String title, List<Runnable> backgroundJobs, Runnable onFinishUiRunnable) {
|
||||
execute(new SimpleTask(title, backgroundJobs, onFinishUiRunnable));
|
||||
}
|
||||
|
||||
public void execute(String title, List<Runnable> backgroundJobs) {
|
||||
execute(new SimpleTask(title, backgroundJobs, null));
|
||||
}
|
||||
|
||||
public void execute(String title, Runnable backgroundRunnable, Runnable onFinishUiRunnable) {
|
||||
execute(new SimpleTask(title, backgroundRunnable, onFinishUiRunnable));
|
||||
}
|
||||
@@ -85,6 +95,7 @@ public class BackgroundExecutor {
|
||||
|
||||
List<Runnable> jobs = task.scheduleJobs();
|
||||
jobsCount = jobs.size();
|
||||
LOG.debug("Starting background task '{}', jobs count: {}", task.getTitle(), jobsCount);
|
||||
if (jobsCount == 1) {
|
||||
jobs.get(0).run();
|
||||
return true;
|
||||
@@ -149,15 +160,19 @@ public class BackgroundExecutor {
|
||||
|
||||
private static final class SimpleTask implements IBackgroundTask {
|
||||
private final String title;
|
||||
private final Runnable runnable;
|
||||
private final List<Runnable> jobs;
|
||||
private final Runnable onFinish;
|
||||
|
||||
public SimpleTask(String title, Runnable runnable, @Nullable Runnable onFinish) {
|
||||
public SimpleTask(String title, List<Runnable> jobs, @Nullable Runnable onFinish) {
|
||||
this.title = title;
|
||||
this.runnable = runnable;
|
||||
this.jobs = jobs;
|
||||
this.onFinish = onFinish;
|
||||
}
|
||||
|
||||
public SimpleTask(String title, Runnable job, @Nullable Runnable onFinish) {
|
||||
this(title, Collections.singletonList(job), onFinish);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
@@ -165,7 +180,7 @@ public class BackgroundExecutor {
|
||||
|
||||
@Override
|
||||
public List<Runnable> scheduleJobs() {
|
||||
return Collections.singletonList(runnable);
|
||||
return jobs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,23 +54,6 @@ public class BackgroundWorker extends SwingWorker<Void, Void> {
|
||||
runJob(cache.getDecompileJob());
|
||||
LOG.debug("Memory usage: After decompile: {}", UiUtils.memoryInfo());
|
||||
|
||||
if (cache.getUnloadJob() != null) {
|
||||
LOG.info("Memory usage: Before unload: {}", UiUtils.memoryInfo());
|
||||
long start = System.nanoTime();
|
||||
runJob(cache.getUnloadJob());
|
||||
cache.setUnloadJob(null);
|
||||
LOG.info("Memory usage: After unload: {}, unload took {} ms", UiUtils.memoryInfo(), (System.nanoTime() - start) / 1000000);
|
||||
}
|
||||
|
||||
if (cache.getRefreshJob() != null) {
|
||||
LOG.info("Memory usage: Before refresh: {}", UiUtils.memoryInfo());
|
||||
long start = System.nanoTime();
|
||||
runJob(cache.getRefreshJob());
|
||||
cache.setRefreshJob(null);
|
||||
LOG.info("Memory usage: After refresh: {}, refresh took {} ms", UiUtils.memoryInfo(),
|
||||
(System.nanoTime() - start) / 1000000);
|
||||
}
|
||||
|
||||
LOG.debug("Memory usage: Before index: {}", UiUtils.memoryInfo());
|
||||
runJob(cache.getIndexJob());
|
||||
LOG.debug("Memory usage: After index: {}", UiUtils.memoryInfo());
|
||||
|
||||
@@ -31,33 +31,53 @@ public class IndexJob extends BackgroundJob {
|
||||
@Override
|
||||
protected void runJob() {
|
||||
JNodeCache nodeCache = cache.getNodeCache();
|
||||
final TextSearchIndex index = new TextSearchIndex(nodeCache);
|
||||
final CodeUsageInfo usageInfo = new CodeUsageInfo(nodeCache);
|
||||
TextSearchIndex index = new TextSearchIndex(nodeCache);
|
||||
CodeUsageInfo usageInfo = new CodeUsageInfo(nodeCache);
|
||||
cache.setTextIndex(index);
|
||||
cache.setUsageInfo(usageInfo);
|
||||
|
||||
for (final JavaClass cls : wrapper.getIncludedClasses()) {
|
||||
addTask(() -> {
|
||||
try {
|
||||
index.indexNames(cls);
|
||||
|
||||
CodeLinesInfo linesInfo = new CodeLinesInfo(cls);
|
||||
List<StringRef> lines = splitLines(cls);
|
||||
|
||||
usageInfo.processClass(cls, linesInfo, lines);
|
||||
if (UiUtils.isFreeMemoryAvailable()) {
|
||||
index.indexCode(cls, linesInfo, lines);
|
||||
} else {
|
||||
index.classCodeIndexSkipped(cls);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Index error in class: {}", cls.getFullName(), e);
|
||||
}
|
||||
});
|
||||
addTask(() -> indexCls(cache, cls));
|
||||
}
|
||||
}
|
||||
|
||||
public static void indexCls(CacheObject cache, JavaClass cls) {
|
||||
try {
|
||||
TextSearchIndex index = cache.getTextIndex();
|
||||
CodeUsageInfo usageInfo = cache.getUsageInfo();
|
||||
if (index == null || usageInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
index.indexNames(cls);
|
||||
|
||||
CodeLinesInfo linesInfo = new CodeLinesInfo(cls);
|
||||
List<StringRef> lines = splitLines(cls);
|
||||
|
||||
usageInfo.processClass(cls, linesInfo, lines);
|
||||
if (UiUtils.isFreeMemoryAvailable()) {
|
||||
index.indexCode(cls, linesInfo, lines);
|
||||
} else {
|
||||
index.classCodeIndexSkipped(cls);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Index error in class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void refreshIndex(CacheObject cache, JavaClass cls) {
|
||||
TextSearchIndex index = cache.getTextIndex();
|
||||
CodeUsageInfo usageInfo = cache.getUsageInfo();
|
||||
if (index == null || usageInfo == null) {
|
||||
return;
|
||||
}
|
||||
index.remove(cls);
|
||||
usageInfo.remove(cls);
|
||||
indexCls(cache, cls);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected List<StringRef> splitLines(JavaClass cls) {
|
||||
protected static List<StringRef> splitLines(JavaClass cls) {
|
||||
List<StringRef> lines = StringRef.split(cls.getCode(), CodeWriter.NL);
|
||||
int size = lines.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package jadx.gui.jobs;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.gui.JadxWrapper;
|
||||
|
||||
public class RefreshJob extends BackgroundJob {
|
||||
|
||||
private Set<JavaClass> refreshClasses;
|
||||
|
||||
public RefreshJob(JadxWrapper jadxWrapper, int threadsCount, Set<JavaClass> refreshClasses) {
|
||||
super(jadxWrapper, threadsCount);
|
||||
this.refreshClasses = refreshClasses;
|
||||
}
|
||||
|
||||
protected void runJob() {
|
||||
for (final JavaClass cls : refreshClasses) {
|
||||
addTask(cls::refresh);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInfoString() {
|
||||
return "Refreshing: ";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package jadx.gui.jobs;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.gui.JadxWrapper;
|
||||
|
||||
public class UnloadJob extends BackgroundJob {
|
||||
|
||||
private Set<JavaClass> refreshClasses;
|
||||
|
||||
public UnloadJob(JadxWrapper jadxWrapper, int threadsCount, Set<JavaClass> refreshClasses) {
|
||||
super(jadxWrapper, threadsCount);
|
||||
this.refreshClasses = refreshClasses;
|
||||
}
|
||||
|
||||
protected void runJob() {
|
||||
for (final JavaClass cls : refreshClasses) {
|
||||
addTask(() -> {
|
||||
cls.unload();
|
||||
cls.getClassNode().deepUnload();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInfoString() {
|
||||
return "Refreshing: ";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,6 +48,10 @@ public class CodeNode extends JNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
public StringRef getLineStr() {
|
||||
return line;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLine() {
|
||||
return lineNum;
|
||||
@@ -72,4 +76,21 @@ public class CodeNode extends JNode {
|
||||
public String makeLongString() {
|
||||
return makeString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof CodeNode)) {
|
||||
return false;
|
||||
}
|
||||
CodeNode codeNode = (CodeNode) o;
|
||||
return jNode.equals(codeNode.jNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return jNode.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,12 +415,6 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void runBackgroundUnloadRefreshAndIndexJobs() {
|
||||
cancelBackgroundJobs();
|
||||
backgroundWorker = new BackgroundWorker(cacheObject, progressPane);
|
||||
backgroundWorker.exec();
|
||||
}
|
||||
|
||||
public synchronized void cancelBackgroundJobs() {
|
||||
backgroundExecutor.cancelAll();
|
||||
if (backgroundWorker != null) {
|
||||
@@ -520,7 +514,7 @@ public class MainWindow extends JFrame {
|
||||
tabbedPane.closeAllTabs();
|
||||
resetCache();
|
||||
treeRoot = null;
|
||||
treeModel.setRoot(treeRoot);
|
||||
treeModel.setRoot(null);
|
||||
treeModel.reload();
|
||||
}
|
||||
|
||||
@@ -1136,6 +1130,10 @@ public class MainWindow extends JFrame {
|
||||
return backgroundWorker;
|
||||
}
|
||||
|
||||
public BackgroundExecutor getBackgroundExecutor() {
|
||||
return backgroundExecutor;
|
||||
}
|
||||
|
||||
public ProgressPanel getProgressPane() {
|
||||
return progressPane;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@@ -23,11 +23,11 @@ import jadx.api.JavaClass;
|
||||
import jadx.api.JavaField;
|
||||
import jadx.api.JavaMethod;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.RenameVisitor;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.gui.jobs.IndexJob;
|
||||
import jadx.gui.jobs.RefreshJob;
|
||||
import jadx.gui.jobs.UnloadJob;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JField;
|
||||
@@ -37,77 +37,59 @@ 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.*;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.TextStandardActions;
|
||||
|
||||
public class RenameDialog extends CommonSearchDialog {
|
||||
public class RenameDialog extends JDialog {
|
||||
private static final long serialVersionUID = -3269715644416902410L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RenameDialog.class);
|
||||
|
||||
protected final transient MainWindow mainWindow;
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
private final transient CacheObject cache;
|
||||
private final transient JNode node;
|
||||
|
||||
private JTextField renameField;
|
||||
|
||||
private CodeArea codeArea;
|
||||
|
||||
private JButton renameBtn;
|
||||
private transient JTextField renameField;
|
||||
|
||||
public RenameDialog(CodeArea codeArea, JNode node) {
|
||||
super(codeArea.getMainWindow());
|
||||
mainWindow = codeArea.getMainWindow();
|
||||
this.codeArea = codeArea;
|
||||
this.mainWindow = codeArea.getMainWindow();
|
||||
this.cache = mainWindow.getCacheObject();
|
||||
this.node = node;
|
||||
if (isDeobfuscationSettingsValid()) {
|
||||
if (checkSettings()) {
|
||||
initUI();
|
||||
registerInitOnOpen();
|
||||
loadWindowPos();
|
||||
} else {
|
||||
LOG.error("Deobfuscation settings are invalid - please enable deobfuscation and disable force rewrite deobfuscation map");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDeobfuscationSettingsValid() {
|
||||
boolean valid = true;
|
||||
String errorMessage = null;
|
||||
private boolean checkSettings() {
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
errorMessage.append(NLS.str("msg.rename_disabled")).append(CodeWriter.NL);
|
||||
|
||||
JadxSettings settings = mainWindow.getSettings();
|
||||
final LangLocale langLocale = settings.getLangLocale();
|
||||
if (settings.isDeobfuscationForceSave()) {
|
||||
valid = false;
|
||||
errorMessage = NLS.str("msg.rename_disabled_force_rewrite_enabled", langLocale);
|
||||
}
|
||||
boolean valid = true;
|
||||
if (!settings.isDeobfuscationOn()) {
|
||||
errorMessage.append(" - ").append(NLS.str("msg.rename_disabled_deobfuscation_disabled")).append(CodeWriter.NL);
|
||||
valid = false;
|
||||
errorMessage = NLS.str("msg.rename_disabled_deobfuscation_disabled", langLocale);
|
||||
}
|
||||
if (errorMessage != null) {
|
||||
showRenameDisabledErrorMessage(langLocale, errorMessage);
|
||||
if (settings.isDeobfuscationForceSave()) {
|
||||
errorMessage.append(" - ").append(NLS.str("msg.rename_disabled_force_rewrite_enabled")).append(CodeWriter.NL);
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
if (valid) {
|
||||
return true;
|
||||
}
|
||||
int result = JOptionPane.showConfirmDialog(mainWindow, errorMessage.toString(),
|
||||
NLS.str("msg.rename_disabled_title"), JOptionPane.OK_CANCEL_OPTION);
|
||||
if (result != JOptionPane.OK_OPTION) {
|
||||
return false;
|
||||
}
|
||||
settings.setDeobfuscationOn(true);
|
||||
settings.setDeobfuscationForceSave(false);
|
||||
settings.sync();
|
||||
|
||||
private void showRenameDisabledErrorMessage(LangLocale langLocale, String message) {
|
||||
JOptionPane.showMessageDialog(
|
||||
mainWindow,
|
||||
message,
|
||||
NLS.str("msg.rename_disabled_title", langLocale),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void openInit() {
|
||||
prepare();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadStart() {
|
||||
renameBtn.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadFinished() {
|
||||
renameBtn.setEnabled(true);
|
||||
mainWindow.reOpenFile();
|
||||
return false; // TODO: can't open dialog, 'node' is replaced with new one after reopen
|
||||
}
|
||||
|
||||
private Path getDeobfMapPath(RootNode root) {
|
||||
@@ -195,29 +177,26 @@ public class RenameDialog extends CommonSearchDialog {
|
||||
}
|
||||
|
||||
private void rename() {
|
||||
long start = System.nanoTime();
|
||||
String renameText = renameField.getText();
|
||||
if (renameText == null || renameText.length() == 0 || codeArea.getText() == null) {
|
||||
return;
|
||||
try {
|
||||
String renameText = renameField.getText();
|
||||
if (renameText == null || renameText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
RootNode root = mainWindow.getWrapper().getDecompiler().getRoot();
|
||||
if (node == null) {
|
||||
LOG.error("rename(): rootNode is null!");
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
if (!refreshDeobfMapFile(renameText, root)) {
|
||||
LOG.error("rename(): refreshDeobfMapFile() failed!");
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
refreshState(root);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Rename failed", e);
|
||||
}
|
||||
RootNode root = mainWindow.getWrapper().getDecompiler().getRoot();
|
||||
if (node == null) {
|
||||
LOG.error("rename(): rootNode is null!");
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
if (!refreshDeobfMapFile(renameText, root)) {
|
||||
LOG.error("rename(): refreshDeobfMapFile() failed!");
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
long refreshStart = System.nanoTime();
|
||||
int classes = refreshState(root);
|
||||
long refreshTime = (System.nanoTime() - refreshStart) / 1000000;
|
||||
long totalTime = (System.nanoTime() - start) / 1000000;
|
||||
LOG.info("refreshState() took {} ms to update state, {} classes will be refreshed in background ({} ms total)", refreshTime,
|
||||
classes,
|
||||
totalTime);
|
||||
dispose();
|
||||
}
|
||||
|
||||
@@ -240,92 +219,60 @@ public class RenameDialog extends CommonSearchDialog {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int refreshState(RootNode rootNode) {
|
||||
private void refreshState(RootNode rootNode) {
|
||||
RenameVisitor renameVisitor = new RenameVisitor();
|
||||
renameVisitor.init(rootNode);
|
||||
|
||||
cache.getNodeCache().refresh(node);
|
||||
JNodeCache nodeCache = cache.getNodeCache();
|
||||
Set<JClass> updatedClasses = node.getJavaNode().getUseIn()
|
||||
.stream()
|
||||
.map(nodeCache::makeFrom)
|
||||
.map(JNode::getRootClass)
|
||||
.collect(Collectors.toSet());
|
||||
updatedClasses.add(node.getRootClass());
|
||||
|
||||
Set<JavaClass> updatedClasses = getUpdatedClasses();
|
||||
|
||||
mainWindow.reloadTree();
|
||||
refreshTabs(mainWindow.getTabbedPane(), updatedClasses);
|
||||
|
||||
if (updatedClasses.size() > 0) {
|
||||
setRefreshTask(updatedClasses);
|
||||
if (!updatedClasses.isEmpty()) {
|
||||
mainWindow.getBackgroundExecutor().execute("Refreshing",
|
||||
Utils.collectionMap(updatedClasses, cls -> () -> refreshJClass(cls)),
|
||||
mainWindow::reloadTree);
|
||||
}
|
||||
|
||||
return updatedClasses.size();
|
||||
}
|
||||
|
||||
private void refreshTabs(TabbedPane tabbedPane, Set<JavaClass> updatedClasses) {
|
||||
for (Map.Entry<JNode, ContentPanel> panel : tabbedPane.getOpenTabs().entrySet()) {
|
||||
ContentPanel contentPanel = panel.getValue();
|
||||
private void refreshJClass(JClass cls) {
|
||||
cls.refresh();
|
||||
IndexJob.refreshIndex(cache, cls.getCls());
|
||||
}
|
||||
|
||||
private void refreshTabs(TabbedPane tabbedPane, Set<JClass> updatedClasses) {
|
||||
for (Map.Entry<JNode, ContentPanel> entry : tabbedPane.getOpenTabs().entrySet()) {
|
||||
ContentPanel contentPanel = entry.getValue();
|
||||
if (contentPanel instanceof ClassCodeContentPanel) {
|
||||
JNode node = panel.getKey();
|
||||
JNode node = entry.getKey();
|
||||
JClass rootClass = node.getRootClass();
|
||||
JavaClass javaClass = rootClass.getCls();
|
||||
if (updatedClasses.contains(javaClass) || node.getRootClass().getCls() == javaClass) {
|
||||
LOG.info("Refreshing rootClass " + javaClass.getRawName());
|
||||
javaClass.unload();
|
||||
javaClass.getClassNode().deepUnload();
|
||||
rootClass.refresh(); // Update code cache
|
||||
if (updatedClasses.contains(rootClass)) {
|
||||
refreshJClass(rootClass);
|
||||
ClassCodeContentPanel codePanel = (ClassCodeContentPanel) contentPanel;
|
||||
CodePanel javaPanel = codePanel.getJavaCodePanel();
|
||||
javaPanel.refresh();
|
||||
tabbedPane.refresh(node);
|
||||
updatedClasses.remove(javaClass);
|
||||
tabbedPane.refresh(rootClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<JavaClass> getUpdatedClasses() {
|
||||
Set<JavaClass> usageClasses = new HashSet<>();
|
||||
CodeUsageInfo usageInfo = cache.getUsageInfo();
|
||||
if (usageInfo != null) {
|
||||
usageInfo.getUsageList(node).forEach((node) -> {
|
||||
JavaClass rootClass = node.getRootClass().getCls();
|
||||
// LOG.info("updateUsages(): Going to update class {}", rootClass.getRealFullName());
|
||||
usageClasses.add(rootClass);
|
||||
});
|
||||
// usageClasses.parallelStream().forEach(JavaClass::refresh);
|
||||
}
|
||||
return usageClasses;
|
||||
}
|
||||
|
||||
private void setRefreshTask(Set<JavaClass> refreshClasses) {
|
||||
UnloadJob unloadJob = new UnloadJob(mainWindow.getWrapper(), mainWindow.getSettings().getThreadsCount(), refreshClasses);
|
||||
RefreshJob refreshJob = new RefreshJob(mainWindow.getWrapper(), mainWindow.getSettings().getThreadsCount(), refreshClasses);
|
||||
LOG.info("Waiting for old unloadJob and refreshJob");
|
||||
while (cache.getUnloadJob() != null || cache.getRefreshJob() != null) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG.info("Old unloadJob and refreshJob finished");
|
||||
cache.setUnloadJob(unloadJob);
|
||||
cache.setRefreshJob(refreshJob);
|
||||
cache.setIndexJob(new IndexJob(mainWindow.getWrapper(), cache, mainWindow.getSettings().getThreadsCount()));
|
||||
mainWindow.runBackgroundUnloadRefreshAndIndexJobs();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected JPanel initButtonsPanel() {
|
||||
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
|
||||
cancelButton.addActionListener(event -> dispose());
|
||||
renameBtn = new JButton(NLS.str("popup.rename"));
|
||||
JButton renameBtn = new JButton(NLS.str("popup.rename"));
|
||||
renameBtn.addActionListener(event -> rename());
|
||||
getRootPane().setDefaultButton(renameBtn);
|
||||
|
||||
progressPane = new ProgressPanel(mainWindow, false);
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||
buttonPane.add(progressPane);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
|
||||
buttonPane.add(Box.createHorizontalGlue());
|
||||
buttonPane.add(renameBtn);
|
||||
@@ -351,18 +298,12 @@ public class RenameDialog extends CommonSearchDialog {
|
||||
renamePane.add(nodeLabel);
|
||||
renamePane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
warnLabel = new JLabel();
|
||||
warnLabel.setForeground(Color.RED);
|
||||
warnLabel.setVisible(false);
|
||||
|
||||
JPanel textPane = new JPanel();
|
||||
textPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS));
|
||||
textPane.add(warnLabel);
|
||||
textPane.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||
textPane.add(renameField);
|
||||
textPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
initCommon();
|
||||
JPanel buttonPane = initButtonsPanel();
|
||||
|
||||
Container contentPane = getContentPane();
|
||||
@@ -376,5 +317,7 @@ public class RenameDialog extends CommonSearchDialog {
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setModalityType(ModalityType.MODELESS);
|
||||
|
||||
mainWindow.getSettings().loadWindowPos(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.gui.jobs.DecompileJob;
|
||||
import jadx.gui.jobs.IndexJob;
|
||||
import jadx.gui.jobs.RefreshJob;
|
||||
import jadx.gui.jobs.UnloadJob;
|
||||
import jadx.gui.ui.SearchDialog;
|
||||
import jadx.gui.utils.search.TextSearchIndex;
|
||||
|
||||
@@ -16,8 +14,6 @@ public class CacheObject {
|
||||
|
||||
private DecompileJob decompileJob;
|
||||
private IndexJob indexJob;
|
||||
private UnloadJob unloadJob;
|
||||
private RefreshJob refreshJob;
|
||||
|
||||
private TextSearchIndex textIndex;
|
||||
private CodeUsageInfo usageInfo;
|
||||
@@ -93,20 +89,4 @@ public class CacheObject {
|
||||
public Set<SearchDialog.SearchOptions> getLastSearchOptions() {
|
||||
return lastSearchOptions;
|
||||
}
|
||||
|
||||
public RefreshJob getRefreshJob() {
|
||||
return refreshJob;
|
||||
}
|
||||
|
||||
public void setRefreshJob(RefreshJob refreshJob) {
|
||||
this.refreshJob = refreshJob;
|
||||
}
|
||||
|
||||
public UnloadJob getUnloadJob() {
|
||||
return unloadJob;
|
||||
}
|
||||
|
||||
public void setUnloadJob(UnloadJob unloadJob) {
|
||||
this.unloadJob = unloadJob;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,4 +70,14 @@ public class CodeUsageInfo {
|
||||
}
|
||||
return usageInfo.getUsageList();
|
||||
}
|
||||
|
||||
public void remove(JavaClass cls) {
|
||||
usageMap.entrySet().removeIf(e -> {
|
||||
if (e.getKey().getJavaNode().getTopParentClass().equals(cls)) {
|
||||
return true;
|
||||
}
|
||||
e.getValue().getUsageList().removeIf(node -> node.getJavaNode().getTopParentClass().equals(cls));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,6 @@ 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;
|
||||
|
||||
@@ -9,46 +9,34 @@ import org.slf4j.LoggerFactory;
|
||||
import io.reactivex.BackpressureStrategy;
|
||||
import io.reactivex.Flowable;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.gui.treemodel.CodeNode;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class CodeIndex<T> implements SearchIndex<T> {
|
||||
public class CodeIndex {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CodeIndex.class);
|
||||
|
||||
private final List<StringRef> keys = new ArrayList<>();
|
||||
private final List<T> values = new ArrayList<>();
|
||||
private final List<CodeNode> values = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void put(String str, T value) {
|
||||
throw new UnsupportedOperationException("CodeIndex.put for string not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void put(StringRef str, T value) {
|
||||
if (str == null || str.length() == 0) {
|
||||
return;
|
||||
}
|
||||
keys.add(str);
|
||||
public synchronized void put(CodeNode value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStringRefSupported() {
|
||||
return true;
|
||||
public void removeForCls(JavaClass cls) {
|
||||
values.removeIf(v -> v.getJavaNode().getTopParentClass().equals(cls));
|
||||
}
|
||||
|
||||
private boolean isMatched(StringRef key, String str, boolean caseInsensitive) {
|
||||
return key.indexOf(str, caseInsensitive) != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flowable<T> search(final String searchStr, final boolean caseInsensitive) {
|
||||
public Flowable<CodeNode> search(final String searchStr, final boolean caseInsensitive) {
|
||||
return Flowable.create(emitter -> {
|
||||
int size = size();
|
||||
LOG.debug("Code search started: {} ...", searchStr);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (isMatched(keys.get(i), searchStr, caseInsensitive)) {
|
||||
emitter.onNext(values.get(i));
|
||||
for (CodeNode node : values) {
|
||||
if (isMatched(node.getLineStr(), searchStr, caseInsensitive)) {
|
||||
emitter.onNext(node);
|
||||
}
|
||||
if (emitter.isCancelled()) {
|
||||
LOG.debug("Code search canceled: {}", searchStr);
|
||||
@@ -60,8 +48,7 @@ public class CodeIndex<T> implements SearchIndex<T> {
|
||||
}, BackpressureStrategy.LATEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return keys.size();
|
||||
return values.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,6 @@ public interface SearchIndex<V> {
|
||||
|
||||
void put(String str, V value);
|
||||
|
||||
void put(StringRef str, V value);
|
||||
|
||||
boolean isStringRefSupported();
|
||||
|
||||
Flowable<V> search(String searchStr, boolean caseInsensitive);
|
||||
|
||||
int size();
|
||||
|
||||
@@ -1,36 +1,25 @@
|
||||
package jadx.gui.utils.search;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import io.reactivex.BackpressureStrategy;
|
||||
import io.reactivex.Flowable;
|
||||
|
||||
public class SimpleIndex<T> implements SearchIndex<T> {
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
|
||||
private final List<String> keys = new ArrayList<>();
|
||||
private final List<T> values = new ArrayList<>();
|
||||
public class SimpleIndex {
|
||||
private final Map<JNode, String> data = new ConcurrentHashMap<>();
|
||||
|
||||
private final Object syncData = new Object();
|
||||
|
||||
@Override
|
||||
public void put(String str, T value) {
|
||||
synchronized (syncData) {
|
||||
keys.add(str);
|
||||
values.add(value);
|
||||
}
|
||||
public void put(String str, JNode value) {
|
||||
data.put(value, str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(StringRef str, T value) {
|
||||
throw new UnsupportedOperationException("StringRef not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStringRefSupported() {
|
||||
return false;
|
||||
public void removeForCls(JavaClass cls) {
|
||||
data.entrySet().removeIf(e -> e.getKey().getJavaNode().getTopParentClass().equals(cls));
|
||||
}
|
||||
|
||||
private boolean isMatched(String str, String searchStr, boolean caseInsensitive) {
|
||||
@@ -41,28 +30,22 @@ public class SimpleIndex<T> implements SearchIndex<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flowable<T> search(final String searchStr, final boolean caseInsensitive) {
|
||||
public Flowable<JNode> search(final String searchStr, final boolean caseInsensitive) {
|
||||
return Flowable.create(emitter -> {
|
||||
synchronized (syncData) {
|
||||
int size = keys.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (isMatched(keys.get(i), searchStr, caseInsensitive)) {
|
||||
emitter.onNext(values.get(i));
|
||||
}
|
||||
if (emitter.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<JNode, String> entry : data.entrySet()) {
|
||||
|
||||
if (isMatched(entry.getValue(), searchStr, caseInsensitive)) {
|
||||
emitter.onNext(entry.getKey());
|
||||
}
|
||||
if (emitter.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
emitter.onComplete();
|
||||
}, BackpressureStrategy.LATEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
synchronized (syncData) {
|
||||
return keys.size();
|
||||
}
|
||||
return data.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,19 +36,19 @@ public class TextSearchIndex {
|
||||
|
||||
private final JNodeCache nodeCache;
|
||||
|
||||
private SearchIndex<JNode> clsNamesIndex;
|
||||
private SearchIndex<JNode> mthSignaturesIndex;
|
||||
private SearchIndex<JNode> fldSignaturesIndex;
|
||||
private SearchIndex<CodeNode> codeIndex;
|
||||
private final SimpleIndex clsNamesIndex;
|
||||
private final SimpleIndex mthSignaturesIndex;
|
||||
private final SimpleIndex fldSignaturesIndex;
|
||||
private final CodeIndex codeIndex;
|
||||
|
||||
private List<JavaClass> skippedClasses = new ArrayList<>();
|
||||
private final List<JavaClass> skippedClasses = new ArrayList<>();
|
||||
|
||||
public TextSearchIndex(JNodeCache nodeCache) {
|
||||
this.nodeCache = nodeCache;
|
||||
this.clsNamesIndex = new SimpleIndex<>();
|
||||
this.mthSignaturesIndex = new SimpleIndex<>();
|
||||
this.fldSignaturesIndex = new SimpleIndex<>();
|
||||
this.codeIndex = new CodeIndex<>();
|
||||
this.clsNamesIndex = new SimpleIndex();
|
||||
this.mthSignaturesIndex = new SimpleIndex();
|
||||
this.fldSignaturesIndex = new SimpleIndex();
|
||||
this.codeIndex = new CodeIndex();
|
||||
}
|
||||
|
||||
public void indexNames(JavaClass cls) {
|
||||
@@ -68,7 +68,6 @@ public class TextSearchIndex {
|
||||
|
||||
public void indexCode(JavaClass cls, CodeLinesInfo linesInfo, List<StringRef> lines) {
|
||||
try {
|
||||
boolean strRefSupported = codeIndex.isStringRefSupported();
|
||||
int count = lines.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
StringRef line = lines.get(i);
|
||||
@@ -78,18 +77,21 @@ public class TextSearchIndex {
|
||||
}
|
||||
int lineNum = i + 1;
|
||||
JavaNode node = linesInfo.getJavaNodeByLine(lineNum);
|
||||
CodeNode codeNode = new CodeNode(nodeCache.makeFrom(node == null ? cls : node), lineNum, line);
|
||||
if (strRefSupported) {
|
||||
codeIndex.put(line, codeNode);
|
||||
} else {
|
||||
codeIndex.put(line.toString(), codeNode);
|
||||
}
|
||||
JNode nodeAtLine = nodeCache.makeFrom(node == null ? cls : node);
|
||||
codeIndex.put(new CodeNode(nodeAtLine, lineNum, line));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to index class: {}", cls, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(JavaClass cls) {
|
||||
this.clsNamesIndex.removeForCls(cls);
|
||||
this.mthSignaturesIndex.removeForCls(cls);
|
||||
this.fldSignaturesIndex.removeForCls(cls);
|
||||
this.codeIndex.removeForCls(cls);
|
||||
}
|
||||
|
||||
public Flowable<JNode> buildSearch(String text, Set<SearchDialog.SearchOptions> options) {
|
||||
boolean ignoreCase = options.contains(IGNORE_CASE);
|
||||
LOG.debug("Building search, ignoreCase: {}", ignoreCase);
|
||||
|
||||
@@ -137,9 +137,9 @@ msg.index_not_initialized=Index not initialized, search will be disabled!
|
||||
msg.project_error_title=Error
|
||||
msg.project_error=Project could not be loaded
|
||||
msg.rename_disabled_title=Rename disabled
|
||||
msg.rename_disabled=Some of rename settings are disabled, please take this into consideration
|
||||
msg.rename_disabled_force_rewrite_enabled=Please disable "Force rewrite deobfuscation map file" option to rename.
|
||||
msg.rename_disabled_deobfuscation_disabled=Please enable deobfuscation to rename.
|
||||
msg.rename_disabled=Some of rename settings are disabled, next options will be changed:
|
||||
msg.rename_disabled_force_rewrite_enabled=Disable "Force rewrite deobfuscation map file" option.
|
||||
msg.rename_disabled_deobfuscation_disabled=Enable deobfuscation.
|
||||
msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
|
||||
|
||||
popup.undo=Undo
|
||||
|
||||
Reference in New Issue
Block a user