mirror of
https://github.com/skylot/jadx.git
synced 2024-11-27 14:40:54 +00:00
perf(gui): on rename unload dependent classes instead recompile
This commit is contained in:
parent
cf918a897f
commit
918585968d
@ -415,6 +415,18 @@ public final class JadxDecompiler implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JavaClass by ClassNode without loading and decompilation
|
||||
*/
|
||||
JavaClass convertClassNode(ClassNode cls) {
|
||||
return classesMap.computeIfAbsent(cls, node -> {
|
||||
if (cls.isInner()) {
|
||||
return new JavaClass(cls, convertClassNode(cls.getParentClass()));
|
||||
}
|
||||
return new JavaClass(cls, this);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable("For not generated classes")
|
||||
@ApiStatus.Internal
|
||||
public JavaClass getJavaClassByNode(ClassNode cls) {
|
||||
@ -555,7 +567,7 @@ public final class JadxDecompiler implements Closeable {
|
||||
return null;
|
||||
}
|
||||
if (obj instanceof ClassNode) {
|
||||
return getJavaClassByNode((ClassNode) obj);
|
||||
return convertClassNode((ClassNode) obj);
|
||||
}
|
||||
if (obj instanceof MethodNode) {
|
||||
return getJavaMethodByNode(((MethodNode) obj));
|
||||
|
@ -63,6 +63,11 @@ public final class JavaClass implements JavaNode {
|
||||
cls.reloadCode();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
listsLoaded = false;
|
||||
cls.unloadCode();
|
||||
}
|
||||
|
||||
public synchronized String getSmali() {
|
||||
return cls.getDisassembledCode();
|
||||
}
|
||||
@ -81,13 +86,14 @@ public final class JavaClass implements JavaNode {
|
||||
}
|
||||
listsLoaded = true;
|
||||
decompile();
|
||||
JadxDecompiler rootDecompiler = getRootDecompiler();
|
||||
|
||||
int inClsCount = cls.getInnerClasses().size();
|
||||
if (inClsCount != 0) {
|
||||
List<JavaClass> list = new ArrayList<>(inClsCount);
|
||||
for (ClassNode inner : cls.getInnerClasses()) {
|
||||
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
||||
JavaClass javaClass = new JavaClass(inner, this);
|
||||
JavaClass javaClass = rootDecompiler.convertClassNode(inner);
|
||||
javaClass.loadLists();
|
||||
list.add(javaClass);
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ public final class ProcessClass {
|
||||
cls.deepUnload();
|
||||
cls.root().runPreDecompileStageForClass(cls);
|
||||
}
|
||||
if (cls.contains(AFlag.CLASS_UNLOADED)) {
|
||||
cls.remove(AFlag.CLASS_UNLOADED);
|
||||
cls.root().runPreDecompileStageForClass(cls);
|
||||
}
|
||||
if (codegen) {
|
||||
if (cls.getState() == GENERATED_AND_UNLOADED) {
|
||||
// allow to run code generation again
|
||||
|
@ -82,6 +82,7 @@ public enum AFlag {
|
||||
RESTART_CODEGEN, // codegen must be executed again
|
||||
RELOAD_AT_CODEGEN_STAGE, // class can't be analyzed at 'process' stage => unload before 'codegen' stage
|
||||
CLASS_DEEP_RELOAD, // perform deep class unload (reload) before process
|
||||
CLASS_UNLOADED, // class was completely unloaded
|
||||
|
||||
DONT_UNLOAD_CLASS, // don't unload class after code generation (only for tests and debug!)
|
||||
}
|
||||
|
@ -274,6 +274,15 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
return decompile(false);
|
||||
}
|
||||
|
||||
public void unloadCode() {
|
||||
if (state == NOT_LOADED) {
|
||||
return;
|
||||
}
|
||||
add(AFlag.CLASS_UNLOADED);
|
||||
unloadFromCache();
|
||||
deepUnload();
|
||||
}
|
||||
|
||||
public void deepUnload() {
|
||||
if (clsData == null) {
|
||||
// manually added class
|
||||
@ -287,17 +296,27 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
innerClasses.forEach(ClassNode::deepUnload);
|
||||
}
|
||||
|
||||
private synchronized ICodeInfo decompile(boolean searchInCache) {
|
||||
private void unloadFromCache() {
|
||||
if (isInner()) {
|
||||
return;
|
||||
}
|
||||
ICodeCache codeCache = root().getCodeCache();
|
||||
ClassNode topParentClass = getTopParentClass();
|
||||
String clsRawName = topParentClass.getRawName();
|
||||
codeCache.remove(getRawName());
|
||||
}
|
||||
|
||||
private synchronized ICodeInfo decompile(boolean searchInCache) {
|
||||
if (isInner()) {
|
||||
return ICodeInfo.EMPTY;
|
||||
}
|
||||
ICodeCache codeCache = root().getCodeCache();
|
||||
String clsRawName = getRawName();
|
||||
if (searchInCache) {
|
||||
ICodeInfo code = codeCache.get(clsRawName);
|
||||
if (code != null && code != ICodeInfo.EMPTY) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
ICodeInfo codeInfo = ProcessClass.generateCode(topParentClass);
|
||||
ICodeInfo codeInfo = ProcessClass.generateCode(this);
|
||||
codeCache.add(clsRawName, codeInfo);
|
||||
return codeInfo;
|
||||
}
|
||||
@ -510,7 +529,8 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
/**
|
||||
* Get all inner and inlined classes recursively
|
||||
*
|
||||
* @param resultClassesSet all identified inner and inlined classes are added to this set
|
||||
* @param resultClassesSet
|
||||
* all identified inner and inlined classes are added to this set
|
||||
*/
|
||||
public void getInnerAndInlinedClassesRecursive(Set<ClassNode> resultClassesSet) {
|
||||
for (ClassNode innerCls : innerClasses) {
|
||||
@ -554,6 +574,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
return parentClass != this;
|
||||
}
|
||||
|
||||
public boolean isTopClass() {
|
||||
return parentClass == this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodNode getClassInitMth() {
|
||||
return searchMethodByShortId("<clinit>()V");
|
||||
|
@ -61,6 +61,15 @@ public class IndexService {
|
||||
indexCls(cls);
|
||||
}
|
||||
|
||||
public synchronized void remove(JavaClass cls) {
|
||||
TextSearchIndex index = cache.getTextIndex();
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
indexSet.remove(cls);
|
||||
index.remove(cls);
|
||||
}
|
||||
|
||||
public boolean isIndexNeeded(JavaClass cls) {
|
||||
return !indexSet.contains(cls);
|
||||
}
|
||||
|
@ -77,6 +77,11 @@ public class JClass extends JLoadableNode implements Comparable<JClass> {
|
||||
update();
|
||||
}
|
||||
|
||||
public synchronized void unload() {
|
||||
cls.unload();
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
removeAllChildren();
|
||||
if (!loaded) {
|
||||
|
@ -42,6 +42,7 @@ import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.rename.RenameVisitor;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.jobs.IndexService;
|
||||
import jadx.gui.jobs.TaskStatus;
|
||||
import jadx.gui.settings.JadxProject;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
@ -97,6 +98,7 @@ public class RenameDialog extends JDialog {
|
||||
refreshState();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Rename failed", e);
|
||||
UiUtils.errorMessage(this, "Rename failed:\n" + Utils.getStackTrace(e));
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
@ -191,7 +193,7 @@ public class RenameDialog extends JDialog {
|
||||
|
||||
if (!updatedTopClasses.isEmpty()) {
|
||||
mainWindow.getBackgroundExecutor().execute("Refreshing",
|
||||
Utils.collectionMap(updatedTopClasses, cls -> () -> refreshJClass(cls)),
|
||||
() -> refreshClasses(updatedTopClasses),
|
||||
(status) -> {
|
||||
if (status == TaskStatus.CANCEL_BY_MEMORY) {
|
||||
mainWindow.showHeapUsageBar();
|
||||
@ -219,12 +221,31 @@ public class RenameDialog extends JDialog {
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshJClass(JClass cls) {
|
||||
try {
|
||||
cls.reload();
|
||||
cache.getIndexService().refreshIndex(cls.getCls());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to reload class: {}", cls.getFullName(), e);
|
||||
private void refreshClasses(Set<JClass> updatedTopClasses) {
|
||||
IndexService indexService = cache.getIndexService();
|
||||
if (updatedTopClasses.size() < 10) {
|
||||
// small batch => reload
|
||||
LOG.debug("Classes to reload: {}", updatedTopClasses.size());
|
||||
for (JClass cls : updatedTopClasses) {
|
||||
try {
|
||||
cls.reload();
|
||||
indexService.refreshIndex(cls.getCls());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to reload class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// big batch => unload
|
||||
LOG.debug("Classes to unload: {}", updatedTopClasses.size());
|
||||
indexService.setComplete(false);
|
||||
for (JClass cls : updatedTopClasses) {
|
||||
try {
|
||||
cls.unload();
|
||||
indexService.remove(cls.getCls());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to unload class: {}", cls.getFullName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user