feat: concat constant strings (#1014)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -97,6 +98,43 @@ public class TypeGen {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String literalToRawString(LiteralArg arg) {
|
||||
ArgType type = arg.getType();
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
long lit = arg.getLiteral();
|
||||
switch (type.getPrimitiveType()) {
|
||||
case BOOLEAN:
|
||||
return lit == 0 ? "false" : "true";
|
||||
case CHAR:
|
||||
return String.valueOf((char) lit);
|
||||
|
||||
case BYTE:
|
||||
case SHORT:
|
||||
case INT:
|
||||
case LONG:
|
||||
return Long.toString(lit);
|
||||
|
||||
case FLOAT:
|
||||
return Float.toString(Float.intBitsToFloat((int) lit));
|
||||
case DOUBLE:
|
||||
return Double.toString(Double.longBitsToDouble(lit));
|
||||
|
||||
case OBJECT:
|
||||
case ARRAY:
|
||||
if (lit != 0) {
|
||||
LOG.warn("Wrong object literal: {} for type: {}", lit, type);
|
||||
return Long.toString(lit);
|
||||
}
|
||||
return "null";
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatShort(long l, boolean cast) {
|
||||
if (l == Short.MAX_VALUE) {
|
||||
return "Short.MAX_VALUE";
|
||||
|
||||
@@ -232,6 +232,10 @@ public abstract class InsnArg extends Typed {
|
||||
return contains(AFlag.THIS);
|
||||
}
|
||||
|
||||
public boolean isConst() {
|
||||
return isLiteral() || (isInsnWrap() && ((InsnWrapArg) this).getWrapInsn().isConstInsn());
|
||||
}
|
||||
|
||||
protected final <T extends InsnArg> T copyCommonParams(T copy) {
|
||||
copy.copyAttributesFrom(this);
|
||||
copy.setParentInsn(parentInsn);
|
||||
|
||||
@@ -4,10 +4,12 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -386,7 +388,7 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
if (!stringArgFound) {
|
||||
// TODO: convert one arg to string using `String.valueOf()`
|
||||
mth.addDebugComment("TODO: convert one arg to string using `String.valueOf()`, args: " + args);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -394,7 +396,8 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
removeStringBuilderInsns(mth, toStrInsn, chain);
|
||||
|
||||
List<InsnArg> dupArgs = Utils.collectionMap(args, InsnArg::duplicate);
|
||||
InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, dupArgs);
|
||||
List<InsnArg> simplifiedArgs = concatConstArgs(dupArgs);
|
||||
InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, simplifiedArgs);
|
||||
concatInsn.setResult(toStrInsn.getResult());
|
||||
concatInsn.add(AFlag.SYNTHETIC);
|
||||
concatInsn.copyAttributesFrom(toStrInsn);
|
||||
@@ -408,6 +411,67 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isConstConcatNeeded(List<InsnArg> args) {
|
||||
boolean prevConst = false;
|
||||
for (InsnArg arg : args) {
|
||||
boolean curConst = arg.isConst();
|
||||
if (curConst && prevConst) {
|
||||
// found 2 consecutive constants
|
||||
return true;
|
||||
}
|
||||
prevConst = curConst;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<InsnArg> concatConstArgs(List<InsnArg> args) {
|
||||
if (!isConstConcatNeeded(args)) {
|
||||
return args;
|
||||
}
|
||||
int size = args.size();
|
||||
List<InsnArg> newArgs = new ArrayList<>(size);
|
||||
List<String> concatList = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
InsnArg arg = args.get(i);
|
||||
String constStr = getConstString(arg);
|
||||
if (constStr != null) {
|
||||
concatList.add(constStr);
|
||||
} else {
|
||||
if (!concatList.isEmpty()) {
|
||||
newArgs.add(getConcatArg(concatList, args, i));
|
||||
concatList.clear();
|
||||
}
|
||||
newArgs.add(arg);
|
||||
}
|
||||
}
|
||||
if (!concatList.isEmpty()) {
|
||||
newArgs.add(getConcatArg(concatList, args, size));
|
||||
}
|
||||
return newArgs;
|
||||
}
|
||||
|
||||
private static InsnArg getConcatArg(List<String> concatList, List<InsnArg> args, int idx) {
|
||||
if (concatList.size() == 1) {
|
||||
return args.get(idx - 1);
|
||||
}
|
||||
String str = Utils.concatStrings(concatList);
|
||||
return InsnArg.wrapArg(new ConstStringNode(str));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getConstString(InsnArg arg) {
|
||||
if (arg.isLiteral()) {
|
||||
return TypeGen.literalToRawString((LiteralArg) arg);
|
||||
}
|
||||
if (arg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
if (wrapInsn instanceof ConstStringNode) {
|
||||
return ((ConstStringNode) wrapInsn).getString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* String concat without assign to variable will cause compilation error */
|
||||
private static void checkResult(MethodNode mth, InsnNode concatInsn) {
|
||||
if (concatInsn.getResult() == null) {
|
||||
|
||||
@@ -101,6 +101,18 @@ public class Utils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String concatStrings(List<String> list) {
|
||||
if (isEmpty(list)) {
|
||||
return "";
|
||||
}
|
||||
if (list.size() == 1) {
|
||||
return list.get(0);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
list.forEach(sb::append);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getStackTrace(Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
return "";
|
||||
|
||||
Reference in New Issue
Block a user