fix: restore fields order if init use other fields (#1235)

This commit is contained in:
Skylot
2021-09-15 21:43:41 +01:00
parent 099acfca1d
commit ea0795c8a9
5 changed files with 198 additions and 36 deletions
@@ -2,8 +2,10 @@ package jadx.core.dex.visitors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -269,9 +271,70 @@ public class ExtractFieldInit extends AbstractVisitor {
return Objects.equals(exclude, Boolean.TRUE);
}
private static void fixFieldsOrder(ClassNode cls, List<FieldInitInfo> fieldsInit) {
private static void fixFieldsOrder(ClassNode cls, List<FieldInitInfo> inits) {
List<FieldNode> orderedFields = processFieldsDependencies(cls, inits);
applyFieldsOrder(cls, orderedFields);
}
private static List<FieldNode> processFieldsDependencies(ClassNode cls, List<FieldInitInfo> inits) {
List<FieldNode> orderedFields = Utils.collectionMap(inits, v -> v.fieldNode);
// collect dependant fields
Map<FieldNode, List<FieldNode>> deps = new HashMap<>(inits.size());
for (FieldInitInfo initInfo : inits) {
IndexInsnNode insn = initInfo.putInsn;
boolean staticField = insn.getType() == InsnType.SPUT;
InsnType useType = staticField ? InsnType.SGET : InsnType.IGET;
insn.visitInsns(subInsn -> {
if (subInsn.getType() == useType) {
FieldInfo fieldInfo = (FieldInfo) ((IndexInsnNode) subInsn).getIndex();
if (fieldInfo.getDeclClass().equals(cls.getClassInfo())) {
FieldNode depField = cls.searchField(fieldInfo);
if (depField != null) {
deps.computeIfAbsent(initInfo.fieldNode, k -> new ArrayList<>())
.add(depField);
}
}
}
});
}
if (deps.isEmpty()) {
return orderedFields;
}
// build new list with deps fields before usage field
List<FieldNode> result = new ArrayList<>();
for (FieldNode field : orderedFields) {
int idx = result.indexOf(field);
List<FieldNode> fieldDeps = deps.get(field);
if (fieldDeps == null) {
if (idx == -1) {
result.add(field);
}
continue;
}
if (idx == -1) {
for (FieldNode depField : fieldDeps) {
if (!result.contains(depField)) {
result.add(depField);
}
}
result.add(field);
continue;
}
for (FieldNode depField : fieldDeps) {
int depIdx = result.indexOf(depField);
if (depIdx == -1) {
result.add(idx, depField);
} else if (depIdx > idx) {
result.remove(depIdx);
result.add(idx, depField);
}
}
}
return result;
}
private static void applyFieldsOrder(ClassNode cls, List<FieldNode> orderedFields) {
List<FieldNode> clsFields = cls.getFields();
List<FieldNode> orderedFields = Utils.collectionMap(fieldsInit, v -> v.fieldNode);
// check if already ordered
boolean ordered = Collections.indexOfSubList(clsFields, orderedFields) != -1;
if (!ordered) {