From 3bdda55102af77c7ca3d149189535d0f4de3afd1 Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 14 Aug 2014 21:34:34 +0400 Subject: [PATCH] core: inline filled array creation --- jadx-core/src/main/java/jadx/core/Jadx.java | 2 + .../main/java/jadx/core/codegen/InsnGen.java | 2 +- .../jadx/core/dex/visitors/ReSugarCode.java | 78 +++++++++++++++++++ .../jadx/core/utils/InstructionRemover.java | 20 +++-- .../jadx/tests/internal/TestFloatValue.java | 2 +- .../annotations/TestVarArgAnnotation.java | 2 +- .../tests/internal/arrays/TestArrayFill.java | 28 +++++++ .../tests/internal/arrays/TestArrayFill2.java | 35 +++++++++ 8 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill2.java diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 0664a9783..42d05c0bb 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -14,6 +14,7 @@ import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.MethodInlineVisitor; import jadx.core.dex.visitors.ModVisitor; import jadx.core.dex.visitors.PrepareForCodeGen; +import jadx.core.dex.visitors.ReSugarCode; import jadx.core.dex.visitors.SimplifyVisitor; import jadx.core.dex.visitors.regions.CheckRegions; import jadx.core.dex.visitors.regions.IfRegionVisitor; @@ -69,6 +70,7 @@ public class Jadx { passes.add(new EnumVisitor()); passes.add(new CodeShrinker()); + passes.add(new ReSugarCode()); if (args.isCFGOutput()) { passes.add(new DotGraphVisitor(outDir, false)); } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 424e26489..c5d8448a4 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -496,7 +496,7 @@ public class InsnGen { useType(code, insn.getResult().getType()); code.add('{'); for (int i = 0; i < c; i++) { - addArg(code, insn.getArg(i)); + addArg(code, insn.getArg(i), false); if (i + 1 < c) { code.add(", "); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java new file mode 100644 index 000000000..c7d17af6a --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java @@ -0,0 +1,78 @@ +package jadx.core.dex.visitors; + +import jadx.core.dex.instructions.InsnType; +import jadx.core.dex.instructions.args.InsnArg; +import jadx.core.dex.instructions.args.LiteralArg; +import jadx.core.dex.nodes.BlockNode; +import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.utils.InstructionRemover; +import jadx.core.utils.exceptions.JadxException; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReSugarCode extends AbstractVisitor { + private static final Logger LOG = LoggerFactory.getLogger(ReSugarCode.class); + + @Override + public void visit(MethodNode mth) throws JadxException { + if (mth.isNoCode()) { + return; + } + InstructionRemover remover = new InstructionRemover(mth); + for (BlockNode block : mth.getBasicBlocks()) { + remover.setBlock(block); + List instructions = block.getInstructions(); + int size = instructions.size(); + for (int i = 0; i < size; i++) { + InsnNode replacedInsn = process(mth, instructions, i, remover); + if (replacedInsn != null) { + instructions.set(i, replacedInsn); + } + } + remover.perform(); + } + } + + private static InsnNode process(MethodNode mth, List instructions, int i, InstructionRemover remover) { + InsnNode insn = instructions.get(i); + switch (insn.getType()) { + case NEW_ARRAY: + return processNewArray(mth, instructions, i, remover); + + default: + return null; + } + } + + /** + * Replace new array and sequence of array-put to new filled-array instruction. + */ + private static InsnNode processNewArray(MethodNode mth, List instructions, int i, InstructionRemover remover) { + InsnNode insn = instructions.get(i); + InsnArg arg = insn.getArg(0); + if (!arg.isLiteral()) { + return null; + } + int len = (int) ((LiteralArg) arg).getLiteral(); + int size = instructions.size(); + if (len <= 0 || i + len >= size || instructions.get(i + len).getType() != InsnType.APUT) { + return null; + } + InsnNode filledArr = new InsnNode(InsnType.FILLED_NEW_ARRAY, len); + filledArr.setResult(insn.getResult()); + for (int j = 0; j < len; j++) { + InsnNode put = instructions.get(i + 1 + j); + if (put.getType() != InsnType.APUT) { + LOG.debug("Not a APUT in expected new filled array: {}, method: {}", put, mth); + return null; + } + filledArr.addArg(put.getArg(2)); + remover.add(put); + } + return filledArr; + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java b/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java index 8be17aca5..447d13201 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java +++ b/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java @@ -26,17 +26,23 @@ public class InstructionRemover { private static final Logger LOG = LoggerFactory.getLogger(InstructionRemover.class); private final MethodNode mth; - private final List insns; private final List toRemove; + private List instrList; - public InstructionRemover(MethodNode mth, BlockNode block) { - this(mth, block.getInstructions()); + public InstructionRemover(MethodNode mth) { + this(mth, null); } - public InstructionRemover(MethodNode mth, List instructions) { + public InstructionRemover(MethodNode mth, BlockNode block) { this.mth = mth; - this.insns = instructions; this.toRemove = new ArrayList(); + if (block != null) { + this.instrList = block.getInstructions(); + } + } + + public void setBlock(BlockNode block) { + this.instrList = block.getInstructions(); } public void add(InsnNode insn) { @@ -47,7 +53,7 @@ public class InstructionRemover { if (toRemove.isEmpty()) { return; } - removeAll(mth, insns, toRemove); + removeAll(mth, instrList, toRemove); toRemove.clear(); } @@ -84,7 +90,7 @@ public class InstructionRemover { } } - // Don't use 'insns.removeAll(toRemove)' because it will remove instructions by content + // Don't use 'instrList.removeAll(toRemove)' because it will remove instructions by content // and here can be several instructions with same content private static void removeAll(MethodNode mth, List insns, List toRemove) { for (InsnNode rem : toRemove) { diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java b/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java index 415e9e506..8de506440 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java +++ b/jadx-core/src/test/java/jadx/tests/internal/TestFloatValue.java @@ -25,7 +25,7 @@ public class TestFloatValue extends InternalJadxTest { String code = cls.getCode().toString(); assertThat(code, not(containsString("1073741824"))); - assertThat(code, containsString("0.55f;")); + 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/annotations/TestVarArgAnnotation.java b/jadx-core/src/test/java/jadx/tests/internal/annotations/TestVarArgAnnotation.java index 8fc8ed205..401f64cbb 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/annotations/TestVarArgAnnotation.java +++ b/jadx-core/src/test/java/jadx/tests/internal/annotations/TestVarArgAnnotation.java @@ -39,7 +39,7 @@ public class TestVarArgAnnotation extends InternalJadxTest { // TODO: assertThat(code, containsString("test1(new int[]{1, 2});")); - assertThat(code, containsString("test2(3, objArr);")); + assertThat(code, containsString("test2(3, new Object[]{\"1\", Integer.valueOf(7)});")); // negative case assertThat(code, containsString("void test3(int[] a) {")); diff --git a/jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill.java b/jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill.java new file mode 100644 index 000000000..811bf9d89 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill.java @@ -0,0 +1,28 @@ +package jadx.tests.internal.arrays; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestArrayFill extends InternalJadxTest { + + public static class TestCls { + + public String[] method() { + return new String[]{"1", "2", "3"}; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("return new String[]{\"1\", \"2\", \"3\"};")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill2.java b/jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill2.java new file mode 100644 index 000000000..6a1fe1110 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/arrays/TestArrayFill2.java @@ -0,0 +1,35 @@ +package jadx.tests.internal.arrays; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestArrayFill2 extends InternalJadxTest { + + public static class TestCls { + + public int[] test(int a) { + return new int[]{1, a + 1, 2}; + } + + public int[] test2(int a) { + return new int[]{1, a++, a * 2}; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("return new int[]{1, a + 1, 2};")); + + // TODO + // assertThat(code, containsString("return new int[]{1, a++, a * 2};")); + } +}