From bd4c61d300af79a66267e2244b038e3b35d5d4d4 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 28 Sep 2013 15:17:20 +0400 Subject: [PATCH] core: fix incorrect float values processing --- .../main/java/jadx/core/codegen/TypeGen.java | 3 +- .../core/dex/instructions/args/ArgType.java | 12 +++- .../dex/instructions/args/LiteralArg.java | 14 ++++- .../dex/visitors/ConstInlinerVisitor.java | 60 ++++++++++++++----- .../jadx/tests/internal/TestArgInline.java | 30 ++++++++++ .../jadx/tests/internal/TestFloatValue.java | 31 ++++++++++ .../internal/TestVariablesDefinitions.java | 42 +++++++++++++ 7 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/internal/TestArgInline.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java diff --git a/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java b/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java index 3102667b..e5c1784c 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java @@ -41,10 +41,11 @@ public class TypeGen { public static String literalToString(long lit, ArgType type) { if (type == null || !type.isTypeKnown()) { String n = Long.toString(lit); - if (Math.abs(lit) > 100) + if (Math.abs(lit) > 100) { n += "; // 0x" + Long.toHexString(lit) + " float:" + Float.intBitsToFloat((int) lit) + " double:" + Double.longBitsToDouble(lit); + } return n; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index a55d1228..a44c9921 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -40,6 +40,10 @@ public abstract class ArgType { PrimitiveType.BOOLEAN, PrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR, PrimitiveType.OBJECT, PrimitiveType.ARRAY); + public static final ArgType NARROW_NUMBERS = unknown( + PrimitiveType.INT, PrimitiveType.FLOAT, + PrimitiveType.BOOLEAN, PrimitiveType.SHORT, PrimitiveType.BYTE, PrimitiveType.CHAR); + public static final ArgType WIDE = unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE); protected int hash; @@ -597,8 +601,14 @@ public abstract class ArgType { public int getRegCount() { if (isPrimitive()) { PrimitiveType type = getPrimitiveType(); - if (type == PrimitiveType.LONG || type == PrimitiveType.DOUBLE) + if (type == PrimitiveType.LONG || type == PrimitiveType.DOUBLE) { return 2; + } else { + return 1; + } + } + if (!isTypeKnown()) { + return 0; } return 1; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java index 3a78ee35..20499bf7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/LiteralArg.java @@ -8,10 +8,20 @@ public final class LiteralArg extends InsnArg { private final long literal; public LiteralArg(long value, ArgType type) { + if (value != 0) { + if (type.isObject()) { + throw new JadxRuntimeException("Wrong literal type: " + type + " for value: " + value); + } else if (!type.isTypeKnown() + && !type.contains(PrimitiveType.LONG) + && !type.contains(PrimitiveType.DOUBLE)) { + ArgType m = ArgType.merge(type, ArgType.NARROW_NUMBERS); + if (m != null) { + type = m; + } + } + } this.literal = value; this.typedVar = new TypedVar(type); - if (literal != 0 && type.isObject()) - throw new RuntimeException("wrong literal type"); } public long getLiteral() { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java index 0c5028f0..4b3f2c44 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java @@ -37,8 +37,12 @@ public class ConstInlinerVisitor extends AbstractVisitor { private static boolean checkInsn(MethodNode mth, BlockNode block, InsnNode insn) { if (insn.getType() == InsnType.CONST) { InsnArg arg = insn.getArg(0); - if (arg.isLiteral() - && insn.getResult().getType().getRegCount() == 1 /* process only narrow types */) { + if (arg.isLiteral()) { + ArgType resType = insn.getResult().getType(); + // make sure arg has correct type + if (!arg.getType().isTypeKnown()) { + arg.merge(resType); + } long lit = ((LiteralArg) arg).getLiteral(); return replaceConst(mth, block, insn, lit); } @@ -61,17 +65,9 @@ public class ConstInlinerVisitor extends AbstractVisitor { if (arg != insn.getResult() && !registerReassignOnPath(block, useBlock, insn)) { // in most cases type not equal arg.getType() // just set unknown type and run type fixer - LiteralArg litArg = InsnArg.lit(literal, ArgType.NARROW); + LiteralArg litArg = InsnArg.lit(literal, ArgType.UNKNOWN); if (useInsn.replaceArg(arg, litArg)) { - // if (useInsn.getType() == InsnType.MOVE) { - // // 'move' became 'const' - // InsnNode constInsn = new InsnNode(mth, InsnType.CONST, 1); - // constInsn.setResult(useInsn.getResult()); - // constInsn.addArg(litArg); - // ModVisitor.replaceInsn(useBlock, useInsn, constInsn); - // fixTypes(mth, constInsn); - // } - fixTypes(mth, useInsn); + fixTypes(mth, useInsn, litArg); replace++; } } @@ -102,7 +98,7 @@ public class ConstInlinerVisitor extends AbstractVisitor { * This is method similar to PostTypeResolver.visit method, * but contains some expensive operations needed only after constant inline */ - private static void fixTypes(MethodNode mth, InsnNode insn) { + private static void fixTypes(MethodNode mth, InsnNode insn, LiteralArg litArg) { switch (insn.getType()) { case CONST: insn.getArg(0).merge(insn.getResult()); @@ -120,13 +116,30 @@ public class ConstInlinerVisitor extends AbstractVisitor { break; } - case IF: + case IF: { IfNode ifnode = (IfNode) insn; if (!ifnode.isZeroCmp()) { - insn.getArg(1).merge(insn.getArg(0)); - insn.getArg(0).merge(insn.getArg(1)); + InsnArg arg0 = insn.getArg(0); + InsnArg arg1 = insn.getArg(1); + if (arg0 == litArg) { + arg0.merge(arg1); + } else { + arg1.merge(arg0); + } } break; + } + case CMP_G: + case CMP_L: { + InsnArg arg0 = insn.getArg(0); + InsnArg arg1 = insn.getArg(1); + if (arg0 == litArg) { + arg0.merge(arg1); + } else { + arg1.merge(arg0); + } + break; + } case RETURN: if (insn.getArgsCount() != 0) { @@ -153,6 +166,21 @@ public class ConstInlinerVisitor extends AbstractVisitor { } break; + case ARITH: + litArg.merge(insn.getResult()); + break; + + case APUT: + case AGET: + if (litArg == insn.getArg(1)) { + litArg.merge(ArgType.INT); + } + break; + + case NEW_ARRAY: + litArg.merge(ArgType.INT); + break; + default: break; } diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestArgInline.java b/jadx-core/src/test/java/jadx/tests/internal/TestArgInline.java new file mode 100644 index 00000000..6e1ef492 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestArgInline.java @@ -0,0 +1,30 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestArgInline extends InternalJadxTest { + + public static class TestCls { + + public void method(int a) { + while (a < 10) { + int b = a + 1; + a = b; + } + } + } + + //@Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + assertThat(code, not(containsString("a = a + 1;"))); + assertThat(code, containsString("a++;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java b/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java new file mode 100644 index 00000000..415e9e50 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java @@ -0,0 +1,31 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestFloatValue extends InternalJadxTest { + + public static class TestCls { + public float[] method() { + float[] fa = {0.55f}; + fa[0] /= 2; + return fa; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, not(containsString("1073741824"))); + assertThat(code, containsString("0.55f;")); + assertThat(code, containsString("fa[0] = fa[0] / 2.0f;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java b/jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java new file mode 100644 index 00000000..66a806fd --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java @@ -0,0 +1,42 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.visitors.DepthTraverser; +import jadx.core.dex.visitors.IDexTreeVisitor; +import jadx.core.utils.exceptions.DecodeException; + +import java.util.List; + +import org.slf4j.Logger; + +public class TestVariablesDefinitions extends InternalJadxTest { + + public static class TestCls { + private static Logger LOG; + private ClassNode cls; + private List passes; + + public void run() { + try { + cls.load(); + for (IDexTreeVisitor visitor : passes) { + DepthTraverser.visit(visitor, cls); + } + } catch (DecodeException e) { + LOG.error("Decode exception: " + cls, e); + } finally { + cls.unload(); + } + } + } + + //@Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + + String code = cls.getCode().toString(); + + System.out.println(code); + } +}