feat: move variable name apply from codegen to new pass (#2422)
This commit is contained in:
@@ -16,6 +16,7 @@ import jadx.core.deobf.DeobfuscatorVisitor;
|
|||||||
import jadx.core.deobf.SaveDeobfMapping;
|
import jadx.core.deobf.SaveDeobfMapping;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.visitors.AnonymousClassVisitor;
|
import jadx.core.dex.visitors.AnonymousClassVisitor;
|
||||||
|
import jadx.core.dex.visitors.ApplyVariableNames;
|
||||||
import jadx.core.dex.visitors.AttachCommentsVisitor;
|
import jadx.core.dex.visitors.AttachCommentsVisitor;
|
||||||
import jadx.core.dex.visitors.AttachMethodDetails;
|
import jadx.core.dex.visitors.AttachMethodDetails;
|
||||||
import jadx.core.dex.visitors.AttachTryCatchVisitor;
|
import jadx.core.dex.visitors.AttachTryCatchVisitor;
|
||||||
@@ -200,6 +201,7 @@ public class Jadx {
|
|||||||
passes.add(new MarkMethodsForInline());
|
passes.add(new MarkMethodsForInline());
|
||||||
}
|
}
|
||||||
passes.add(new ProcessVariables());
|
passes.add(new ProcessVariables());
|
||||||
|
passes.add(new ApplyVariableNames());
|
||||||
passes.add(new PrepareForCodeGen());
|
passes.add(new PrepareForCodeGen());
|
||||||
if (args.isCfgOutput()) {
|
if (args.isCfgOutput()) {
|
||||||
passes.add(DotGraphVisitor.dumpRegions());
|
passes.add(DotGraphVisitor.dumpRegions());
|
||||||
|
|||||||
@@ -2,57 +2,22 @@ package jadx.core.codegen;
|
|||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import jadx.core.Consts;
|
|
||||||
import jadx.core.deobf.NameMapper;
|
import jadx.core.deobf.NameMapper;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
|
||||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
|
||||||
import jadx.core.dex.info.MethodInfo;
|
|
||||||
import jadx.core.dex.instructions.InvokeNode;
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
|
||||||
import jadx.core.dex.instructions.args.CodeVar;
|
import jadx.core.dex.instructions.args.CodeVar;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
|
||||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
|
||||||
import jadx.core.dex.instructions.args.NamedArg;
|
import jadx.core.dex.instructions.args.NamedArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.instructions.args.SSAVar;
|
import jadx.core.dex.instructions.args.SSAVar;
|
||||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.StringUtils;
|
|
||||||
import jadx.core.utils.Utils;
|
|
||||||
|
|
||||||
public class NameGen {
|
public class NameGen {
|
||||||
|
|
||||||
private static final Map<String, String> OBJ_ALIAS;
|
|
||||||
|
|
||||||
private final Set<String> varNames = new HashSet<>();
|
|
||||||
private final MethodNode mth;
|
private final MethodNode mth;
|
||||||
private final boolean fallback;
|
private final boolean fallback;
|
||||||
|
private final Set<String> varNames = new HashSet<>();
|
||||||
static {
|
|
||||||
OBJ_ALIAS = Utils.newConstStringMap(
|
|
||||||
Consts.CLASS_STRING, "str",
|
|
||||||
Consts.CLASS_CLASS, "cls",
|
|
||||||
Consts.CLASS_THROWABLE, "th",
|
|
||||||
Consts.CLASS_OBJECT, "obj",
|
|
||||||
"java.util.Iterator", "it",
|
|
||||||
"java.lang.Boolean", "bool",
|
|
||||||
"java.lang.Short", "sh",
|
|
||||||
"java.lang.Integer", "num",
|
|
||||||
"java.lang.Character", "ch",
|
|
||||||
"java.lang.Byte", "b",
|
|
||||||
"java.lang.Float", "f",
|
|
||||||
"java.lang.Long", "l",
|
|
||||||
"java.lang.Double", "d",
|
|
||||||
"java.lang.StringBuilder", "sb",
|
|
||||||
"java.lang.Exception", "exc");
|
|
||||||
}
|
|
||||||
|
|
||||||
public NameGen(MethodNode mth, ClassGen classGen) {
|
public NameGen(MethodNode mth, ClassGen classGen) {
|
||||||
this.mth = mth;
|
this.mth = mth;
|
||||||
@@ -99,9 +64,9 @@ public class NameGen {
|
|||||||
if (fallback) {
|
if (fallback) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
name = getUniqueVarName(name);
|
String uniqName = getUniqueVarName(name);
|
||||||
arg.setName(name);
|
arg.setName(uniqName);
|
||||||
return name;
|
return uniqName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String useArg(RegisterArg arg) {
|
public String useArg(RegisterArg arg) {
|
||||||
@@ -132,13 +97,10 @@ public class NameGen {
|
|||||||
|
|
||||||
private String makeArgName(CodeVar var) {
|
private String makeArgName(CodeVar var) {
|
||||||
String name = var.getName();
|
String name = var.getName();
|
||||||
if (name == null) {
|
if (NameMapper.isValidAndPrintable(name)) {
|
||||||
name = guessName(var);
|
return name;
|
||||||
}
|
}
|
||||||
if (!NameMapper.isValidAndPrintable(name)) {
|
return getFallbackName(var);
|
||||||
name = getFallbackName(var);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFallbackName(CodeVar var) {
|
private String getFallbackName(CodeVar var) {
|
||||||
@@ -152,153 +114,4 @@ public class NameGen {
|
|||||||
private String getFallbackName(RegisterArg arg) {
|
private String getFallbackName(RegisterArg arg) {
|
||||||
return "r" + arg.getRegNum();
|
return "r" + arg.getRegNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String guessName(CodeVar var) {
|
|
||||||
List<SSAVar> ssaVars = var.getSsaVars();
|
|
||||||
if (ssaVars != null && !ssaVars.isEmpty()) {
|
|
||||||
// TODO: use all vars for better name generation
|
|
||||||
SSAVar ssaVar = ssaVars.get(0);
|
|
||||||
if (ssaVar != null && ssaVar.getName() == null) {
|
|
||||||
RegisterArg assignArg = ssaVar.getAssign();
|
|
||||||
InsnNode assignInsn = assignArg.getParentInsn();
|
|
||||||
if (assignInsn != null) {
|
|
||||||
String name = makeNameFromInsn(assignInsn);
|
|
||||||
if (name != null && NameMapper.isValidAndPrintable(name)) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return makeNameForType(var.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeNameForType(ArgType type) {
|
|
||||||
if (type.isPrimitive()) {
|
|
||||||
return type.getPrimitiveType().getShortName().toLowerCase();
|
|
||||||
}
|
|
||||||
if (type.isArray()) {
|
|
||||||
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
|
||||||
}
|
|
||||||
return makeNameForObject(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeNameForObject(ArgType type) {
|
|
||||||
if (type.isGenericType()) {
|
|
||||||
return StringUtils.escape(type.getObject().toLowerCase());
|
|
||||||
}
|
|
||||||
if (type.isObject()) {
|
|
||||||
String alias = getAliasForObject(type.getObject());
|
|
||||||
if (alias != null) {
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
return makeNameForCheckedClass(ClassInfo.fromType(mth.root(), type));
|
|
||||||
}
|
|
||||||
return StringUtils.escape(type.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeNameForCheckedClass(ClassInfo classInfo) {
|
|
||||||
String shortName = classInfo.getAliasShortName();
|
|
||||||
String vName = fromName(shortName);
|
|
||||||
if (vName != null) {
|
|
||||||
return vName;
|
|
||||||
}
|
|
||||||
String lower = StringUtils.escape(shortName.toLowerCase());
|
|
||||||
if (shortName.equals(lower)) {
|
|
||||||
return lower + "Var";
|
|
||||||
}
|
|
||||||
return lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeNameForClass(ClassInfo classInfo) {
|
|
||||||
String alias = getAliasForObject(classInfo.getFullName());
|
|
||||||
if (alias != null) {
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
return makeNameForCheckedClass(classInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fromName(String name) {
|
|
||||||
if (name == null || name.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (name.toUpperCase().equals(name)) {
|
|
||||||
// all characters are upper case
|
|
||||||
return name.toLowerCase();
|
|
||||||
}
|
|
||||||
String v1 = Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
|
||||||
if (!v1.equals(name)) {
|
|
||||||
return v1;
|
|
||||||
}
|
|
||||||
if (name.length() < 3) {
|
|
||||||
return name + "Var";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getAliasForObject(String name) {
|
|
||||||
return OBJ_ALIAS.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeNameFromInsn(InsnNode insn) {
|
|
||||||
switch (insn.getType()) {
|
|
||||||
case INVOKE:
|
|
||||||
InvokeNode inv = (InvokeNode) insn;
|
|
||||||
return makeNameFromInvoke(inv.getCallMth());
|
|
||||||
|
|
||||||
case CONSTRUCTOR:
|
|
||||||
ConstructorInsn co = (ConstructorInsn) insn;
|
|
||||||
MethodNode callMth = mth.root().getMethodUtils().resolveMethod(co);
|
|
||||||
if (callMth != null && callMth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {
|
|
||||||
// don't use name of anonymous class
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return makeNameForClass(co.getClassType());
|
|
||||||
|
|
||||||
case ARRAY_LENGTH:
|
|
||||||
return "length";
|
|
||||||
|
|
||||||
case ARITH:
|
|
||||||
case TERNARY:
|
|
||||||
case CAST:
|
|
||||||
for (InsnArg arg : insn.getArguments()) {
|
|
||||||
if (arg.isInsnWrap()) {
|
|
||||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
|
||||||
String wName = makeNameFromInsn(wrapInsn);
|
|
||||||
if (wName != null) {
|
|
||||||
return wName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeNameFromInvoke(MethodInfo callMth) {
|
|
||||||
String name = callMth.getAlias();
|
|
||||||
ClassInfo declClass = callMth.getDeclClass();
|
|
||||||
if ("getInstance".equals(name)) {
|
|
||||||
// e.g. Cipher.getInstance
|
|
||||||
return makeNameForClass(declClass);
|
|
||||||
}
|
|
||||||
if (name.startsWith("get") || name.startsWith("set")) {
|
|
||||||
return fromName(name.substring(3));
|
|
||||||
}
|
|
||||||
if ("iterator".equals(name)) {
|
|
||||||
return "it";
|
|
||||||
}
|
|
||||||
if ("toString".equals(name)) {
|
|
||||||
return makeNameForClass(declClass);
|
|
||||||
}
|
|
||||||
if ("forName".equals(name) && declClass.getType().equals(ArgType.CLASS)) {
|
|
||||||
return OBJ_ALIAS.get(Consts.CLASS_CLASS);
|
|
||||||
}
|
|
||||||
if (name.startsWith("to")) {
|
|
||||||
return fromName(name.substring(2));
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,13 +59,11 @@ public class SSAVar implements Comparable<SSAVar> {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull RegisterArg getAssign() {
|
||||||
public RegisterArg getAssign() {
|
|
||||||
return assign;
|
return assign;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable InsnNode getAssignInsn() {
|
||||||
public InsnNode getAssignInsn() {
|
|
||||||
return assign.getParentInsn();
|
return assign.getParentInsn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.core.Consts;
|
||||||
|
import jadx.core.deobf.NameMapper;
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
|
import jadx.core.dex.info.ClassInfo;
|
||||||
|
import jadx.core.dex.info.MethodInfo;
|
||||||
|
import jadx.core.dex.instructions.InvokeNode;
|
||||||
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
import jadx.core.dex.instructions.args.CodeVar;
|
||||||
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
|
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||||
|
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.InsnNode;
|
||||||
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
import jadx.core.dex.nodes.RootNode;
|
||||||
|
import jadx.core.dex.visitors.regions.variables.ProcessVariables;
|
||||||
|
import jadx.core.utils.StringUtils;
|
||||||
|
import jadx.core.utils.Utils;
|
||||||
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
|
|
||||||
|
@JadxVisitor(
|
||||||
|
name = "ApplyVariableNames",
|
||||||
|
desc = "Try to guess variable name from usage",
|
||||||
|
runAfter = {
|
||||||
|
ProcessVariables.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class ApplyVariableNames extends AbstractVisitor {
|
||||||
|
|
||||||
|
private static final Map<String, String> OBJ_ALIAS = Utils.newConstStringMap(
|
||||||
|
Consts.CLASS_STRING, "str",
|
||||||
|
Consts.CLASS_CLASS, "cls",
|
||||||
|
Consts.CLASS_THROWABLE, "th",
|
||||||
|
Consts.CLASS_OBJECT, "obj",
|
||||||
|
"java.util.Iterator", "it",
|
||||||
|
"java.util.HashMap", "map",
|
||||||
|
"java.lang.Boolean", "bool",
|
||||||
|
"java.lang.Short", "sh",
|
||||||
|
"java.lang.Integer", "num",
|
||||||
|
"java.lang.Character", "ch",
|
||||||
|
"java.lang.Byte", "b",
|
||||||
|
"java.lang.Float", "f",
|
||||||
|
"java.lang.Long", "l",
|
||||||
|
"java.lang.Double", "d",
|
||||||
|
"java.lang.StringBuilder", "sb",
|
||||||
|
"java.lang.Exception", "exc");
|
||||||
|
|
||||||
|
private static final Set<String> GOOD_VAR_NAMES = Set.of(
|
||||||
|
"size", "length", "list", "map", "next");
|
||||||
|
private static final List<String> INVOKE_PREFIXES = List.of(
|
||||||
|
"get", "set", "to", "parse", "read", "format");
|
||||||
|
|
||||||
|
private RootNode root;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(RootNode root) throws JadxException {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MethodNode mth) throws JadxException {
|
||||||
|
for (SSAVar ssaVar : mth.getSVars()) {
|
||||||
|
CodeVar codeVar = ssaVar.getCodeVar();
|
||||||
|
String newName = guessName(codeVar);
|
||||||
|
if (newName != null) {
|
||||||
|
codeVar.setName(newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String guessName(CodeVar var) {
|
||||||
|
if (var.isThis()) {
|
||||||
|
return RegisterArg.THIS_ARG_NAME;
|
||||||
|
}
|
||||||
|
if (!var.isDeclared()) {
|
||||||
|
// name is not used in code
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (NameMapper.isValidAndPrintable(var.getName())) {
|
||||||
|
// the current name is valid, keep it
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<SSAVar> ssaVars = var.getSsaVars();
|
||||||
|
if (Utils.notEmpty(ssaVars)) {
|
||||||
|
boolean mthArg = ssaVars.stream().anyMatch(ssaVar -> ssaVar.getAssign().contains(AFlag.METHOD_ARGUMENT));
|
||||||
|
if (mthArg) {
|
||||||
|
// for method args use defined type and ignore usage
|
||||||
|
return makeNameForType(var.getType());
|
||||||
|
}
|
||||||
|
for (SSAVar ssaVar : ssaVars) {
|
||||||
|
String name = makeNameForSSAVar(ssaVar);
|
||||||
|
if (name != null) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return makeNameForType(var.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String makeNameForSSAVar(SSAVar ssaVar) {
|
||||||
|
String ssaVarName = ssaVar.getName();
|
||||||
|
if (ssaVarName != null) {
|
||||||
|
return ssaVarName;
|
||||||
|
}
|
||||||
|
InsnNode assignInsn = ssaVar.getAssignInsn();
|
||||||
|
if (assignInsn != null) {
|
||||||
|
String name = makeNameFromInsn(ssaVar, assignInsn);
|
||||||
|
if (NameMapper.isValidAndPrintable(name)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeNameFromInsn(SSAVar ssaVar, InsnNode insn) {
|
||||||
|
switch (insn.getType()) {
|
||||||
|
case INVOKE:
|
||||||
|
return makeNameFromInvoke(ssaVar, (InvokeNode) insn);
|
||||||
|
|
||||||
|
case CONSTRUCTOR:
|
||||||
|
ConstructorInsn co = (ConstructorInsn) insn;
|
||||||
|
MethodNode callMth = root.getMethodUtils().resolveMethod(co);
|
||||||
|
if (callMth != null && callMth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {
|
||||||
|
// don't use name of anonymous class
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return makeNameForClass(co.getClassType());
|
||||||
|
|
||||||
|
case ARRAY_LENGTH:
|
||||||
|
return "length";
|
||||||
|
|
||||||
|
case ARITH:
|
||||||
|
case TERNARY:
|
||||||
|
case CAST:
|
||||||
|
for (InsnArg arg : insn.getArguments()) {
|
||||||
|
if (arg.isInsnWrap()) {
|
||||||
|
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||||
|
String wName = makeNameFromInsn(ssaVar, wrapInsn);
|
||||||
|
if (wName != null) {
|
||||||
|
return wName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeNameForType(ArgType type) {
|
||||||
|
if (type.isPrimitive()) {
|
||||||
|
return type.getPrimitiveType().getShortName().toLowerCase();
|
||||||
|
}
|
||||||
|
if (type.isArray()) {
|
||||||
|
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
||||||
|
}
|
||||||
|
return makeNameForObject(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeNameForObject(ArgType type) {
|
||||||
|
if (type.isGenericType()) {
|
||||||
|
return StringUtils.escape(type.getObject().toLowerCase());
|
||||||
|
}
|
||||||
|
if (type.isObject()) {
|
||||||
|
String alias = getAliasForObject(type.getObject());
|
||||||
|
if (alias != null) {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
return makeNameForCheckedClass(ClassInfo.fromType(root, type));
|
||||||
|
}
|
||||||
|
return StringUtils.escape(type.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeNameForCheckedClass(ClassInfo classInfo) {
|
||||||
|
String shortName = classInfo.getAliasShortName();
|
||||||
|
String vName = fromName(shortName);
|
||||||
|
if (vName != null) {
|
||||||
|
return vName;
|
||||||
|
}
|
||||||
|
String lower = StringUtils.escape(shortName.toLowerCase());
|
||||||
|
if (shortName.equals(lower)) {
|
||||||
|
return lower + "Var";
|
||||||
|
}
|
||||||
|
return lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeNameForClass(ClassInfo classInfo) {
|
||||||
|
String alias = getAliasForObject(classInfo.getFullName());
|
||||||
|
if (alias != null) {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
return makeNameForCheckedClass(classInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fromName(String name) {
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (name.toUpperCase().equals(name)) {
|
||||||
|
// all characters are upper case
|
||||||
|
return name.toLowerCase();
|
||||||
|
}
|
||||||
|
String v1 = Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||||
|
if (!v1.equals(name)) {
|
||||||
|
return v1;
|
||||||
|
}
|
||||||
|
if (name.length() < 3) {
|
||||||
|
return name + "Var";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getAliasForObject(String name) {
|
||||||
|
return OBJ_ALIAS.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String makeNameFromInvoke(SSAVar ssaVar, InvokeNode inv) {
|
||||||
|
MethodInfo callMth = inv.getCallMth();
|
||||||
|
String name = callMth.getAlias();
|
||||||
|
ClassInfo declClass = callMth.getDeclClass();
|
||||||
|
if ("getInstance".equals(name)) {
|
||||||
|
// e.g. Cipher.getInstance
|
||||||
|
return makeNameForClass(declClass);
|
||||||
|
}
|
||||||
|
String shortName = cutPrefix(name);
|
||||||
|
if (shortName != null) {
|
||||||
|
return fromName(shortName);
|
||||||
|
}
|
||||||
|
if ("iterator".equals(name)) {
|
||||||
|
return "it";
|
||||||
|
}
|
||||||
|
if ("toString".equals(name)) {
|
||||||
|
return makeNameForClass(declClass);
|
||||||
|
}
|
||||||
|
if ("forName".equals(name) && declClass.getType().equals(ArgType.CLASS)) {
|
||||||
|
return OBJ_ALIAS.get(Consts.CLASS_CLASS);
|
||||||
|
}
|
||||||
|
// use method name as a variable name not the best idea in most cases
|
||||||
|
if (!GOOD_VAR_NAMES.contains(name)) {
|
||||||
|
String typeName = makeNameForType(ssaVar.getCodeVar().getType());
|
||||||
|
if (!typeName.equalsIgnoreCase(name)) {
|
||||||
|
return typeName + StringUtils.capitalizeFirstChar(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable String cutPrefix(String name) {
|
||||||
|
for (String prefix : INVOKE_PREFIXES) {
|
||||||
|
if (name.startsWith(prefix)) {
|
||||||
|
return name.substring(prefix.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "ApplyVariableNames";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -331,6 +331,7 @@ public class ProcessVariables extends AbstractVisitor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
parentInsn.add(AFlag.DECLARE_VAR);
|
parentInsn.add(AFlag.DECLARE_VAR);
|
||||||
|
var.getCodeVar().setDeclared(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -483,4 +483,11 @@ public class StringUtils {
|
|||||||
}
|
}
|
||||||
return Float.toString(f) + 'f';
|
return Float.toString(f) + 'f';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String capitalizeFirstChar(String str) {
|
||||||
|
if (isEmpty(str)) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,11 +133,11 @@ public class TestIfCodeStyle extends SmaliTest {
|
|||||||
.oneOf(c -> c.doesNotContain("else").countString(8, "return;"),
|
.oneOf(c -> c.doesNotContain("else").countString(8, "return;"),
|
||||||
c -> c.countString(1, "else").countString(7, "return;"))
|
c -> c.countString(1, "else").countString(7, "return;"))
|
||||||
.containsLines(2,
|
.containsLines(2,
|
||||||
"if (readInt < 0) {",
|
"if (i < 0) {",
|
||||||
indent() + "if (dataPosition > Integer.MAX_VALUE - readInt) {",
|
indent() + "if (iDataPosition > Integer.MAX_VALUE - i) {",
|
||||||
indent(2) + "throw new RuntimeException(\"Overflow in the size of parcelable\");",
|
indent(2) + "throw new RuntimeException(\"Overflow in the size of parcelable\");",
|
||||||
indent() + "}",
|
indent() + "}",
|
||||||
indent() + "parcel.setDataPosition(dataPosition + readInt);",
|
indent() + "parcel.setDataPosition(iDataPosition + i);",
|
||||||
indent() + "return;",
|
indent() + "return;",
|
||||||
"}");
|
"}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class TestInnerAssign3 extends SmaliTest {
|
|||||||
disableCompilation();
|
disableCompilation();
|
||||||
assertThat(getClassNodeFromSmali())
|
assertThat(getClassNodeFromSmali())
|
||||||
.code()
|
.code()
|
||||||
.containsOne("(testMethod = (testClass1 = null).testMethod()) == null")
|
.containsOne("(testClass2TestMethod = (testClass1 = null).testMethod()) == null")
|
||||||
.containsOne("testClass1.testField != null");
|
.containsOne("testClass1.testField != null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ public class TestLambdaInstance3 extends RaungTest {
|
|||||||
assertThat(getClassNode(TestCls.class))
|
assertThat(getClassNode(TestCls.class))
|
||||||
.code()
|
.code()
|
||||||
.doesNotContain("this::get")
|
.doesNotContain("this::get")
|
||||||
.containsOne("return (TestCls) of::get;");
|
.containsOne("return (TestCls) lazyOf::get;");
|
||||||
// TODO: type inference set type for 'of' to Memoized and cast incorrectly removed
|
// TODO: type inference set type for 'lazyOf' to Memoized and cast incorrectly removed
|
||||||
// .containsOne("Memoized)");
|
// .containsOne("Memoized)");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +49,6 @@ public class TestLambdaInstance3 extends RaungTest {
|
|||||||
assertThat(getClassNodeFromRaung())
|
assertThat(getClassNodeFromRaung())
|
||||||
.code()
|
.code()
|
||||||
.doesNotContain("this::get")
|
.doesNotContain("this::get")
|
||||||
.containsOne(" of::get");
|
.containsOne(" lazyOf::get");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ public class TestLoopRestore extends SmaliTest {
|
|||||||
assertThat(getClassNodeFromSmali())
|
assertThat(getClassNodeFromSmali())
|
||||||
.code()
|
.code()
|
||||||
.containsOne("try {")
|
.containsOne("try {")
|
||||||
.containsOne("for (byte b : digest) {");
|
.containsOne("for (byte b : bArrDigest) {");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ public class TestTryCatchFinally15 extends SmaliTest {
|
|||||||
assertThat(getClassNodeFromSmali())
|
assertThat(getClassNodeFromSmali())
|
||||||
.code()
|
.code()
|
||||||
.doesNotContain("parcel = Parcel.obtain();")
|
.doesNotContain("parcel = Parcel.obtain();")
|
||||||
.containsOne("this.zza.transact(i, parcel, obtain, 0);");
|
.containsOne("this.zza.transact(i, parcel, parcelObtain, 0);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class TestPrimitiveConversion2 extends SmaliTest {
|
|||||||
.doesNotContain("z2 == 0")
|
.doesNotContain("z2 == 0")
|
||||||
.doesNotContain("z2 | 2")
|
.doesNotContain("z2 | 2")
|
||||||
.containsOne("(z2 ? 1 : 0) | 2")
|
.containsOne("(z2 ? 1 : 0) | 2")
|
||||||
.containsOne("if (z2 && formatCurrency != null) {")
|
.containsOne("if (z2 && currency != null) {")
|
||||||
.containsOne("i = 1;");
|
.containsOne("i = 1;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ public class TestPrimitivesInIf extends IntegrationTest {
|
|||||||
noDebugInfo();
|
noDebugInfo();
|
||||||
assertThat(getClassNode(TestCls.class))
|
assertThat(getClassNode(TestCls.class))
|
||||||
.code()
|
.code()
|
||||||
.containsOne("short parseShort = Short.parseShort(str);");
|
.containsOne("short s = Short.parseShort(str);");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ public class TestTypeResolver16 extends SmaliTest {
|
|||||||
public void test() {
|
public void test() {
|
||||||
assertThat(getClassNode(TestCls.class))
|
assertThat(getClassNode(TestCls.class))
|
||||||
.code()
|
.code()
|
||||||
.containsOne("(List<T>) list");
|
.containsOne("(List<T>) listUnion");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSmali() {
|
public void testSmali() {
|
||||||
assertThat(getClassNodeFromSmali())
|
assertThat(getClassNodeFromSmali())
|
||||||
.code()
|
.code()
|
||||||
.containsOne("(List<T>) list");
|
.containsOne("(List<T>) listUnion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class TestTypeResolver17 extends SmaliTest {
|
|||||||
disableCompilation();
|
disableCompilation();
|
||||||
assertThat(getClassNodeFromSmali())
|
assertThat(getClassNodeFromSmali())
|
||||||
.code()
|
.code()
|
||||||
.containsOne("Cursor cursor = null;")
|
.containsOne("Cursor cursorQuery = null;")
|
||||||
.doesNotContain("(AutoCloseable autoCloseable = ");
|
.doesNotContain("(AutoCloseable autoCloseable = ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ public class TestVariablesInLoop extends SmaliTest {
|
|||||||
public void test() {
|
public void test() {
|
||||||
assertThat(getClassNodeFromSmali())
|
assertThat(getClassNodeFromSmali())
|
||||||
.code()
|
.code()
|
||||||
.containsOne("int i;")
|
.containsOne("int iMth;")
|
||||||
.countString(2, "i = 0;")
|
.countString(2, "iMth = 0;")
|
||||||
.doesNotContain("i3");
|
.doesNotContain("i2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user