perf(gui): on rename unload dependent classes instead recompile

This commit is contained in:
Skylot 2021-11-13 14:53:51 +00:00
parent cf918a897f
commit 918585968d
No known key found for this signature in database
GPG Key ID: 1E23F5B52567AA39
8 changed files with 96 additions and 14 deletions

View File

@ -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));

View File

@ -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);
}

View File

@ -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

View File

@ -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!)
}

View File

@ -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");

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);
}
}
}
}