refactor: allow store unresolved fields in ConstStorage (#2119)
This commit is contained in:
@@ -2,10 +2,8 @@ package jadx.core.dex.info;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -18,31 +16,32 @@ import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.IFieldInfoRef;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public class ConstStorage {
|
||||
|
||||
private static final class ValueStorage {
|
||||
private final Map<Object, FieldNode> values = new ConcurrentHashMap<>();
|
||||
private final Map<Object, IFieldInfoRef> values = new ConcurrentHashMap<>();
|
||||
private final Set<Object> duplicates = new HashSet<>();
|
||||
|
||||
public Map<Object, FieldNode> getValues() {
|
||||
public Map<Object, IFieldInfoRef> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public FieldNode get(Object key) {
|
||||
public IFieldInfoRef get(Object key) {
|
||||
return values.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this value is duplicated
|
||||
*/
|
||||
public boolean put(Object value, FieldNode fld) {
|
||||
public boolean put(Object value, IFieldInfoRef fld) {
|
||||
if (duplicates.contains(value)) {
|
||||
values.remove(value);
|
||||
return true;
|
||||
}
|
||||
FieldNode prev = values.put(value, fld);
|
||||
IFieldInfoRef prev = values.put(value, fld);
|
||||
if (prev != null) {
|
||||
values.remove(value);
|
||||
duplicates.add(value);
|
||||
@@ -56,14 +55,13 @@ public class ConstStorage {
|
||||
}
|
||||
|
||||
void removeForCls(ClassNode cls) {
|
||||
Iterator<Entry<Object, FieldNode>> it = values.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<Object, FieldNode> entry = it.next();
|
||||
FieldNode field = entry.getValue();
|
||||
if (field.getParentClass().equals(cls)) {
|
||||
it.remove();
|
||||
values.entrySet().removeIf(entry -> {
|
||||
IFieldInfoRef field = entry.getValue();
|
||||
if (field instanceof FieldNode) {
|
||||
return ((FieldNode) field).getParentClass().equals(cls);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,18 +75,30 @@ public class ConstStorage {
|
||||
this.replaceEnabled = args.isReplaceConsts();
|
||||
}
|
||||
|
||||
public void processConstFields(ClassNode cls, List<FieldNode> staticFields) {
|
||||
public void processConstFields(List<FieldNode> staticFields) {
|
||||
if (!replaceEnabled || staticFields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (FieldNode f : staticFields) {
|
||||
Object value = getFieldConstValue(f);
|
||||
if (value != null) {
|
||||
addConstField(cls, f, value, f.getAccessFlags().isPublic());
|
||||
addConstField(f, value, f.getAccessFlags().isPublic());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addConstField(FieldNode fld, Object value, boolean isPublic) {
|
||||
if (isPublic) {
|
||||
addGlobalConstField(fld, value);
|
||||
} else {
|
||||
getClsValues(fld.getParentClass()).put(value, fld);
|
||||
}
|
||||
}
|
||||
|
||||
public void addGlobalConstField(IFieldInfoRef fld, Object value) {
|
||||
globalValues.put(value, fld);
|
||||
}
|
||||
|
||||
public static @Nullable Object getFieldConstValue(FieldNode fld) {
|
||||
AccessInfo accFlags = fld.getAccessFlags();
|
||||
if (accFlags.isStatic() && accFlags.isFinal()) {
|
||||
@@ -105,20 +115,11 @@ public class ConstStorage {
|
||||
globalValues.removeForCls(cls);
|
||||
}
|
||||
|
||||
private void addConstField(ClassNode cls, FieldNode fld, Object value, boolean isPublic) {
|
||||
if (isPublic) {
|
||||
globalValues.put(value, fld);
|
||||
} else {
|
||||
getClsValues(cls).put(value, fld);
|
||||
}
|
||||
}
|
||||
|
||||
private ValueStorage getClsValues(ClassNode cls) {
|
||||
return classes.computeIfAbsent(cls, c -> new ValueStorage());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldNode getConstField(ClassNode cls, Object value, boolean searchGlobal) {
|
||||
public @Nullable IFieldInfoRef getConstField(ClassNode cls, Object value, boolean searchGlobal) {
|
||||
if (!replaceEnabled) {
|
||||
return null;
|
||||
}
|
||||
@@ -137,7 +138,7 @@ public class ConstStorage {
|
||||
while (current != null) {
|
||||
ValueStorage classValues = classes.get(current);
|
||||
if (classValues != null) {
|
||||
FieldNode field = classValues.get(value);
|
||||
IFieldInfoRef field = classValues.get(value);
|
||||
if (field != null) {
|
||||
if (foundInGlobal) {
|
||||
return null;
|
||||
@@ -182,8 +183,7 @@ public class ConstStorage {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldNode getConstFieldByLiteralArg(ClassNode cls, LiteralArg arg) {
|
||||
public @Nullable IFieldInfoRef getConstFieldByLiteralArg(ClassNode cls, LiteralArg arg) {
|
||||
if (!replaceEnabled) {
|
||||
return null;
|
||||
}
|
||||
@@ -225,7 +225,7 @@ public class ConstStorage {
|
||||
return resourcesNames;
|
||||
}
|
||||
|
||||
public Map<Object, FieldNode> getGlobalConstFields() {
|
||||
public Map<Object, IFieldInfoRef> getGlobalConstFields() {
|
||||
return globalValues.getValues();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@ import java.util.Objects;
|
||||
import jadx.api.plugins.input.data.IFieldRef;
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.IFieldInfoRef;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
public final class FieldInfo {
|
||||
public final class FieldInfo implements IFieldInfoRef {
|
||||
|
||||
private final ClassInfo declClass;
|
||||
private final String name;
|
||||
@@ -76,6 +77,11 @@ public final class FieldInfo {
|
||||
return name.equals(other.name) && type.equals(other.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldInfo getFieldInfo() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -257,7 +257,7 @@ public class ClassNode extends NotificationAttrNode
|
||||
}
|
||||
try {
|
||||
// process const fields
|
||||
root().getConstValues().processConstFields(this, staticFields);
|
||||
root().getConstValues().processConstFields(staticFields);
|
||||
} catch (Exception e) {
|
||||
this.addWarnComment("Failed to load initial values for static fields", e);
|
||||
}
|
||||
@@ -513,17 +513,15 @@ public class ClassNode extends NotificationAttrNode
|
||||
fields.add(fld);
|
||||
}
|
||||
|
||||
public FieldNode getConstField(Object obj) {
|
||||
public @Nullable IFieldInfoRef getConstField(Object obj) {
|
||||
return getConstField(obj, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldNode getConstField(Object obj, boolean searchGlobal) {
|
||||
public @Nullable IFieldInfoRef getConstField(Object obj, boolean searchGlobal) {
|
||||
return root().getConstValues().getConstField(this, obj, searchGlobal);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FieldNode getConstFieldByLiteralArg(LiteralArg arg) {
|
||||
public @Nullable IFieldInfoRef getConstFieldByLiteralArg(LiteralArg arg) {
|
||||
return root().getConstValues().getConstFieldByLiteralArg(this, arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.utils.ListUtils;
|
||||
|
||||
public class FieldNode extends NotificationAttrNode implements ICodeNode {
|
||||
public class FieldNode extends NotificationAttrNode implements ICodeNode, IFieldInfoRef {
|
||||
|
||||
private final ClassNode parentClass;
|
||||
private final FieldInfo fieldInfo;
|
||||
@@ -46,6 +46,7 @@ public class FieldNode extends NotificationAttrNode implements ICodeNode {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldInfo getFieldInfo() {
|
||||
return fieldInfo;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
|
||||
/**
|
||||
* Common interface for FieldInfo and FieldNode
|
||||
*/
|
||||
public interface IFieldInfoRef {
|
||||
FieldInfo getFieldInfo();
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.IFieldInfoRef;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.finaly.MarkFinallyVisitor;
|
||||
@@ -82,7 +82,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
}
|
||||
case CONST_STR: {
|
||||
String s = ((ConstStringNode) insn).getString();
|
||||
FieldNode f = mth.getParentClass().getConstField(s);
|
||||
IFieldInfoRef f = mth.getParentClass().getConstField(s);
|
||||
if (f == null) {
|
||||
InsnNode copy = insn.copyWithoutResult();
|
||||
constArg = InsnArg.wrapArg(copy);
|
||||
@@ -90,7 +90,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
InsnNode constGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
constArg = InsnArg.wrapArg(constGet);
|
||||
constArg.setType(ArgType.STRING);
|
||||
onSuccess = () -> f.addUseIn(mth);
|
||||
onSuccess = () -> ModVisitor.addFieldUsage(f, mth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -251,7 +251,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
return false;
|
||||
}
|
||||
// arg replaced, made some optimizations
|
||||
FieldNode fieldNode = null;
|
||||
IFieldInfoRef fieldNode = null;
|
||||
ArgType litArgType = litArg.getType();
|
||||
if (litArgType.isTypeKnown()) {
|
||||
fieldNode = mth.getParentClass().getConstFieldByLiteralArg(litArg);
|
||||
@@ -261,7 +261,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
||||
if (fieldNode != null) {
|
||||
IndexInsnNode sgetInsn = new IndexInsnNode(InsnType.SGET, fieldNode.getFieldInfo(), 0);
|
||||
if (litArg.wrapInstruction(mth, sgetInsn) != null) {
|
||||
fieldNode.addUseIn(mth);
|
||||
ModVisitor.addFieldUsage(fieldNode, mth);
|
||||
}
|
||||
} else {
|
||||
addExplicitCast(useInsn, litArg);
|
||||
|
||||
@@ -44,6 +44,7 @@ import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.IFieldInfoRef;
|
||||
import jadx.core.dex.nodes.IMethodDetails;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -239,10 +240,10 @@ public class ModVisitor extends AbstractVisitor {
|
||||
int[] keys = insn.getKeys();
|
||||
int len = keys.length;
|
||||
for (int k = 0; k < len; k++) {
|
||||
FieldNode f = parentClass.getConstField(keys[k]);
|
||||
IFieldInfoRef f = parentClass.getConstField(keys[k]);
|
||||
if (f != null) {
|
||||
insn.modifyKey(k, f);
|
||||
f.addUseIn(mth);
|
||||
addFieldUsage(f, mth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,7 +314,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
return new EncodedValue(EncodedType.ENCODED_ARRAY, listVal);
|
||||
}
|
||||
FieldNode constField = parentCls.getConstField(encodedValue.getValue());
|
||||
IFieldInfoRef constField = parentCls.getConstField(encodedValue.getValue());
|
||||
if (constField != null) {
|
||||
return new EncodedValue(EncodedType.ENCODED_FIELD, constField.getFieldInfo());
|
||||
}
|
||||
@@ -321,7 +322,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private static void replaceConst(MethodNode mth, ClassNode parentClass, BlockNode block, int i, InsnNode insn) {
|
||||
FieldNode f;
|
||||
IFieldInfoRef f;
|
||||
if (insn.getType() == InsnType.CONST_STR) {
|
||||
String s = ((ConstStringNode) insn).getString();
|
||||
f = parentClass.getConstField(s);
|
||||
@@ -335,7 +336,7 @@ public class ModVisitor extends AbstractVisitor {
|
||||
InsnNode inode = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
inode.setResult(insn.getResult());
|
||||
replaceInsn(mth, block, i, inode);
|
||||
f.addUseIn(mth);
|
||||
addFieldUsage(f, mth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,11 +346,11 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
InsnArg litArg = arithNode.getArg(1);
|
||||
if (litArg.isLiteral()) {
|
||||
FieldNode f = parentClass.getConstFieldByLiteralArg((LiteralArg) litArg);
|
||||
IFieldInfoRef f = parentClass.getConstFieldByLiteralArg((LiteralArg) litArg);
|
||||
if (f != null) {
|
||||
InsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
if (arithNode.replaceArg(litArg, InsnArg.wrapArg(fGet))) {
|
||||
f.addUseIn(mth);
|
||||
addFieldUsage(f, mth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,11 +567,11 @@ public class ModVisitor extends AbstractVisitor {
|
||||
InsnNode filledArr = new FilledNewArrayNode(elType, list.size());
|
||||
filledArr.setResult(newArrayNode.getResult().duplicate());
|
||||
for (LiteralArg arg : list) {
|
||||
FieldNode f = mth.getParentClass().getConstFieldByLiteralArg(arg);
|
||||
IFieldInfoRef f = mth.getParentClass().getConstFieldByLiteralArg(arg);
|
||||
if (f != null) {
|
||||
InsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
filledArr.addArg(InsnArg.wrapArg(fGet));
|
||||
f.addUseIn(mth);
|
||||
addFieldUsage(f, mth);
|
||||
} else {
|
||||
filledArr.addArg(arg.duplicate());
|
||||
}
|
||||
@@ -607,4 +608,10 @@ public class ModVisitor extends AbstractVisitor {
|
||||
}
|
||||
block.copyAttributeFrom(insn, AType.CODE_COMMENTS); // save comment
|
||||
}
|
||||
|
||||
public static void addFieldUsage(IFieldInfoRef fieldData, MethodNode mth) {
|
||||
if (fieldData instanceof FieldNode) {
|
||||
((FieldNode) fieldData).addUseIn(mth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.IFieldInfoRef;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
|
||||
@@ -167,11 +167,11 @@ public class ReplaceNewArray extends AbstractVisitor {
|
||||
|
||||
private static InsnArg replaceConstInArg(MethodNode mth, InsnArg valueArg) {
|
||||
if (valueArg.isLiteral()) {
|
||||
FieldNode f = mth.getParentClass().getConstFieldByLiteralArg((LiteralArg) valueArg);
|
||||
IFieldInfoRef f = mth.getParentClass().getConstFieldByLiteralArg((LiteralArg) valueArg);
|
||||
if (f != null) {
|
||||
InsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||
InsnArg arg = InsnArg.wrapArg(fGet);
|
||||
f.addUseIn(mth);
|
||||
ModVisitor.addFieldUsage(f, mth);
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,18 +170,17 @@ public class AndroidResourcesUtils {
|
||||
private static Map<Integer, FieldNode> fillResFieldsMap(ClassNode resCls) {
|
||||
Map<Integer, FieldNode> resFieldsMap = new HashMap<>();
|
||||
ConstStorage constStorage = resCls.root().getConstValues();
|
||||
Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields();
|
||||
for (Map.Entry<Object, FieldNode> entry : constFields.entrySet()) {
|
||||
Object key = entry.getKey();
|
||||
FieldNode field = entry.getValue();
|
||||
AccessInfo accessFlags = field.getAccessFlags();
|
||||
if (field.getType().equals(ArgType.INT)
|
||||
&& accessFlags.isStatic()
|
||||
&& accessFlags.isFinal()
|
||||
constStorage.getGlobalConstFields().forEach((key, field) -> {
|
||||
if (field.getFieldInfo().getType().equals(ArgType.INT)
|
||||
&& field instanceof FieldNode
|
||||
&& key instanceof Integer) {
|
||||
resFieldsMap.put((Integer) key, field);
|
||||
FieldNode fldNode = (FieldNode) field;
|
||||
AccessInfo accessFlags = fldNode.getAccessFlags();
|
||||
if (accessFlags.isStatic() && accessFlags.isFinal()) {
|
||||
resFieldsMap.put((Integer) key, fldNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return resFieldsMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.IFieldInfoRef;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.BetterName;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -417,7 +418,8 @@ public class ResTableParser extends CommonBinaryParser implements IResParser {
|
||||
if (typeName.equals("style")) {
|
||||
return origKeyName;
|
||||
}
|
||||
FieldNode constField = root.getConstValues().getGlobalConstFields().get(resRef);
|
||||
IFieldInfoRef fldRef = root.getConstValues().getGlobalConstFields().get(resRef);
|
||||
FieldNode constField = fldRef instanceof FieldNode ? (FieldNode) fldRef : null;
|
||||
String resAlias = getResAlias(resRef, origKeyName, constField);
|
||||
resStorage.addRename(resRef, resAlias);
|
||||
if (constField != null) {
|
||||
|
||||
Reference in New Issue
Block a user