Skip to content

Commit

Permalink
feat: initial deboxing implementation (#717)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Jul 23, 2019
1 parent 3ae8359 commit fd7d08c
Show file tree
Hide file tree
Showing 15 changed files with 302 additions and 31 deletions.
2 changes: 2 additions & 0 deletions jadx-core/src/main/java/jadx/core/Jadx.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import jadx.core.dex.visitors.ClassModifier;
import jadx.core.dex.visitors.ConstInlineVisitor;
import jadx.core.dex.visitors.ConstructorVisitor;
import jadx.core.dex.visitors.DeboxingVisitor;
import jadx.core.dex.visitors.DependencyCollector;
import jadx.core.dex.visitors.DotGraphVisitor;
import jadx.core.dex.visitors.EnumVisitor;
Expand Down Expand Up @@ -87,6 +88,7 @@ public static List<IDexTreeVisitor> getPassesList(JadxArgs args) {
passes.add(new DebugInfoApplyVisitor());
}

passes.add(new DeboxingVisitor());
passes.add(new ModVisitor());
passes.add(new CodeShrinkVisitor());
passes.add(new ReSugarCode());
Expand Down
8 changes: 4 additions & 4 deletions jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public void encodeValue(CodeWriter code, Object val) {
if (val instanceof String) {
code.add(getStringUtils().unescapeString((String) val));
} else if (val instanceof Integer) {
code.add(TypeGen.formatInteger((Integer) val));
code.add(TypeGen.formatInteger((Integer) val, false));
} else if (val instanceof Character) {
code.add(getStringUtils().unescapeChar((Character) val));
} else if (val instanceof Boolean) {
Expand All @@ -157,11 +157,11 @@ public void encodeValue(CodeWriter code, Object val) {
} else if (val instanceof Double) {
code.add(TypeGen.formatDouble((Double) val));
} else if (val instanceof Long) {
code.add(TypeGen.formatLong((Long) val));
code.add(TypeGen.formatLong((Long) val, false));
} else if (val instanceof Short) {
code.add(TypeGen.formatShort((Short) val));
code.add(TypeGen.formatShort((Short) val, false));
} else if (val instanceof Byte) {
code.add(TypeGen.formatByte((Byte) val));
code.add(TypeGen.formatByte((Byte) val, false));
} else if (val instanceof ArgType) {
classGen.useType(code, (ArgType) val);
code.add(".class");
Expand Down
2 changes: 1 addition & 1 deletion jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public void declareVar(CodeWriter code, CodeVar codeVar) {
}

private String lit(LiteralArg arg) {
return TypeGen.literalToString(arg.getLiteral(), arg.getType(), mth, fallback);
return TypeGen.literalToString(arg, mth, fallback);
}

private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
Expand Down
45 changes: 30 additions & 15 deletions jadx-core/src/main/java/jadx/core/codegen/TypeGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import org.slf4j.LoggerFactory;

import jadx.core.deobf.NameMapper;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.IDexNode;
import jadx.core.utils.StringUtils;
Expand All @@ -28,16 +30,26 @@ public static String signature(ArgType type) {
return stype.getShortName();
}

/**
* Convert literal arg to string (preferred method)
*/
public static String literalToString(LiteralArg arg, IDexNode dexNode, boolean fallback) {
return literalToString(arg.getLiteral(), arg.getType(),
dexNode.root().getStringUtils(),
fallback,
arg.contains(AFlag.EXPLICIT_PRIMITIVE_TYPE));
}

/**
* Convert literal value to string according to value type
*
* @throws JadxRuntimeException for incorrect type or literal value
*/
public static String literalToString(long lit, ArgType type, IDexNode dexNode, boolean fallback) {
return literalToString(lit, type, dexNode.root().getStringUtils(), fallback);
return literalToString(lit, type, dexNode.root().getStringUtils(), fallback, false);
}

public static String literalToString(long lit, ArgType type, StringUtils stringUtils, boolean fallback) {
public static String literalToString(long lit, ArgType type, StringUtils stringUtils, boolean fallback, boolean cast) {
if (type == null || !type.isTypeKnown()) {
String n = Long.toString(lit);
if (fallback && Math.abs(lit) > 100) {
Expand Down Expand Up @@ -65,13 +77,13 @@ public static String literalToString(long lit, ArgType type, StringUtils stringU
}
return stringUtils.unescapeChar(ch);
case BYTE:
return formatByte(lit);
return formatByte(lit, cast);
case SHORT:
return formatShort(lit);
return formatShort(lit, cast);
case INT:
return formatInteger(lit);
return formatInteger(lit, cast);
case LONG:
return formatLong(lit);
return formatLong(lit, cast);
case FLOAT:
return formatFloat(Float.intBitsToFloat((int) lit));
case DOUBLE:
Expand All @@ -90,46 +102,49 @@ public static String literalToString(long lit, ArgType type, StringUtils stringU
}
}

public static String formatShort(long l) {
public static String formatShort(long l, boolean cast) {
if (l == Short.MAX_VALUE) {
return "Short.MAX_VALUE";
}
if (l == Short.MIN_VALUE) {
return "Short.MIN_VALUE";
}
return Long.toString(l);
String str = Long.toString(l);
return cast ? "(short) " + str : str;
}

public static String formatByte(long l) {
public static String formatByte(long l, boolean cast) {
if (l == Byte.MAX_VALUE) {
return "Byte.MAX_VALUE";
}
if (l == Byte.MIN_VALUE) {
return "Byte.MIN_VALUE";
}
return Long.toString(l);
String str = Long.toString(l);
return cast ? "(byte) " + str : str;
}

public static String formatInteger(long l) {
public static String formatInteger(long l, boolean cast) {
if (l == Integer.MAX_VALUE) {
return "Integer.MAX_VALUE";
}
if (l == Integer.MIN_VALUE) {
return "Integer.MIN_VALUE";
}
return Long.toString(l);
String str = Long.toString(l);
return cast ? "(int) " + str : str;
}

public static String formatLong(long l) {
public static String formatLong(long l, boolean cast) {
if (l == Long.MAX_VALUE) {
return "Long.MAX_VALUE";
}
if (l == Long.MIN_VALUE) {
return "Long.MIN_VALUE";
}
String str = Long.toString(l);
if (Math.abs(l) >= Integer.MAX_VALUE) {
str += 'L';
if (cast || Math.abs(l) >= Integer.MAX_VALUE) {
return str + 'L';
}
return str;
}
Expand Down
5 changes: 5 additions & 0 deletions jadx-core/src/main/java/jadx/core/dex/attributes/AFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,10 @@ public enum AFlag {

EXPLICIT_GENERICS,

/**
* Use constants with explicit type: cast '(byte) 1' or type letter '7L'
*/
EXPLICIT_PRIMITIVE_TYPE,

INCONSISTENT_CODE, // warning about incorrect decompilation
}
7 changes: 3 additions & 4 deletions jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ private MethodInfo(DexNode dex, int mthIndex) {

private MethodInfo(ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {
this.name = name;
alias = name;
aliasFromPreset = false;
this.alias = name;
this.aliasFromPreset = false;
this.declClass = declClass;

this.args = args;
this.retType = retType;
shortId = makeSignature(true);
this.shortId = makeSignature(true);
}

public static MethodInfo externalMth(ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package jadx.core.dex.instructions;

import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.RegisterArg;

public interface CallMthInterface {

MethodInfo getCallMth();

RegisterArg getInstanceArg();
}
15 changes: 15 additions & 0 deletions jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package jadx.core.dex.instructions;

import org.jetbrains.annotations.Nullable;

import com.android.dx.io.instructions.DecodedInstruction;

import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.Utils;
Expand Down Expand Up @@ -51,6 +54,18 @@ public MethodInfo getCallMth() {
return mth;
}

@Override
@Nullable
public RegisterArg getInstanceArg() {
if (type != InvokeType.STATIC && getArgsCount() > 0) {
InsnArg firstArg = getArg(0);
if (firstArg.isRegister()) {
return ((RegisterArg) firstArg);
}
}
return null;
}

@Override
public InsnNode copy() {
return copyCommonParams(new InvokeNode(mth, type, getArgsCount()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public boolean equals(Object o) {
@Override
public String toString() {
try {
String value = TypeGen.literalToString(literal, getType(), DEF_STRING_UTILS, true);
String value = TypeGen.literalToString(literal, getType(), DEF_STRING_UTILS, true, false);
if (getType().equals(ArgType.BOOLEAN) && (value.equals("true") || value.equals("false"))) {
return value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ public ArgType getType() {
if (sVar != null) {
return sVar.getTypeInfo().getType();
}
LOG.warn("Register type unknown, SSA variable not initialized: r{}", regNum);
return type;
return ArgType.UNKNOWN;
}

public ArgType getInitType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public MethodInfo getCallMth() {
return callMth;
}

@Override
public RegisterArg getInstanceArg() {
return instanceArg;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public void visit(MethodNode mth) throws JadxException {
if (mth.isNoCode()) {
return;
}
process(mth);
}

public static void process(MethodNode mth) {
List<InsnNode> toRemove = new ArrayList<>();
for (BlockNode block : mth.getBasicBlocks()) {
toRemove.clear();
Expand Down Expand Up @@ -175,17 +179,19 @@ private static boolean replaceArg(MethodNode mth, RegisterArg arg, InsnArg const

if (constArg.isLiteral()) {
long literal = ((LiteralArg) constArg).getLiteral();
ArgType argType = arg.getInitType();
ArgType argType = arg.getType();
if (argType == ArgType.UNKNOWN) {
argType = arg.getInitType();
}
if (argType.isObject() && literal != 0) {
argType = ArgType.NARROW_NUMBERS;
}
LiteralArg litArg = InsnArg.lit(literal, argType);
litArg.copyAttributesFrom(constArg);
if (!useInsn.replaceArg(arg, litArg)) {
return false;
}
// arg replaced, made some optimizations
litArg.setType(arg.getInitType());

FieldNode fieldNode = null;
ArgType litArgType = litArg.getType();
if (litArgType.isTypeKnown()) {
Expand Down
Loading

0 comments on commit fd7d08c

Please sign in to comment.