refactor: add method info caching to speed up initial loading

This commit is contained in:
Skylot
2020-08-07 17:38:08 +01:00
parent 1560284831
commit 7fed5534eb
20 changed files with 233 additions and 89 deletions
@@ -11,6 +11,8 @@ public class InfoStorage {
private final Map<FieldInfo, FieldInfo> fields = new HashMap<>();
// use only one MethodInfo instance
private final Map<MethodInfo, MethodInfo> uniqueMethods = new HashMap<>();
// can contain same method with different ids (from different dex files)
private final Map<Integer, MethodInfo> methods = new HashMap<>();
public ClassInfo getCls(ArgType type) {
return classes.get(type);
@@ -23,6 +25,18 @@ public class InfoStorage {
}
}
public MethodInfo getByUniqId(int id) {
synchronized (methods) {
return methods.get(id);
}
}
public void putByUniqId(int id, MethodInfo mth) {
synchronized (methods) {
methods.put(id, mth);
}
}
public MethodInfo putMethod(MethodInfo newMth) {
synchronized (uniqueMethods) {
MethodInfo prev = uniqueMethods.get(newMth);
@@ -5,7 +5,7 @@ import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import jadx.api.plugins.input.data.IMethodData;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.core.codegen.TypeGen;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.RootNode;
@@ -31,18 +31,27 @@ public final class MethodInfo implements Comparable<MethodInfo> {
this.shortId = makeShortId(name, argTypes, retType);
}
public static MethodInfo fromData(RootNode root, IMethodData methodData) {
ArgType parentClsType = ArgType.parse(methodData.getParentClassType());
public static MethodInfo fromRef(RootNode root, IMethodRef methodRef) {
InfoStorage infoStorage = root.getInfoStorage();
int uniqId = methodRef.getUniqId();
MethodInfo prevMth = infoStorage.getByUniqId(uniqId);
if (prevMth != null) {
return prevMth;
}
methodRef.load();
ArgType parentClsType = ArgType.parse(methodRef.getParentClassType());
ClassInfo parentClass = ClassInfo.fromType(root, parentClsType);
ArgType returnType = ArgType.parse(methodData.getReturnType());
List<ArgType> args = Utils.collectionMap(methodData.getArgTypes(), ArgType::parse);
MethodInfo newMth = new MethodInfo(parentClass, methodData.getName(), args, returnType);
return root.getInfoStorage().putMethod(newMth);
ArgType returnType = ArgType.parse(methodRef.getReturnType());
List<ArgType> args = Utils.collectionMap(methodRef.getArgTypes(), ArgType::parse);
MethodInfo newMth = new MethodInfo(parentClass, methodRef.getName(), args, returnType);
MethodInfo uniqMth = infoStorage.putMethod(newMth);
infoStorage.putByUniqId(uniqId, uniqMth);
return uniqMth;
}
public static MethodInfo fromDetails(RootNode rootNode, ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {
public static MethodInfo fromDetails(RootNode root, ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {
MethodInfo newMth = new MethodInfo(declClass, name, args, retType);
return rootNode.getInfoStorage().putMethod(newMth);
return root.getInfoStorage().putMethod(newMth);
}
public String makeSignature(boolean includeRetType) {
@@ -521,8 +521,8 @@ public class InsnDecoder {
}
private InsnNode invoke(InsnData insn, InvokeType type, boolean isRange) {
MethodInfo mth = MethodInfo.fromData(root, insn.getIndexAsMethod());
return new InvokeNode(mth, insn, type, isRange);
MethodInfo mthInfo = MethodInfo.fromRef(root, insn.getIndexAsMethod());
return new InvokeNode(mthInfo, insn, type, isRange);
}
private InsnNode arrayGet(InsnData insn, ArgType argType) {
@@ -81,8 +81,8 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
public ClassNode(RootNode root, IClassData cls) {
this.root = root;
this.clsInfo = ClassInfo.fromType(root, ArgType.object(cls.getType()));
initialLoad(cls);
this.clsData = cls.copy();
initialLoad(clsData);
}
private void initialLoad(IClassData cls) {
@@ -82,8 +82,8 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
return methodNode;
}
public MethodNode(ClassNode classNode, IMethodData mthData) {
this.mthInfo = MethodInfo.fromData(classNode.root(), mthData);
private MethodNode(ClassNode classNode, IMethodData mthData) {
this.mthInfo = MethodInfo.fromRef(classNode.root(), mthData.getMethodRef());
this.parentClass = classNode;
this.accFlags = new AccessInfo(mthData.getAccessFlags(), AFType.METHOD);
this.methodIsVirtual = !mthData.isDirect();
@@ -104,7 +104,7 @@ public class UsageInfoVisitor extends AbstractVisitor {
case METHOD_REF:
insnData.decode();
MethodNode methodNode = root.resolveMethod(MethodInfo.fromData(root, insnData.getIndexAsMethod()));
MethodNode methodNode = root.resolveMethod(MethodInfo.fromRef(root, insnData.getIndexAsMethod()));
if (methodNode != null) {
usageInfo.methodUse(mth, methodNode);
}