Commit 88ccba16 authored by Skylot's avatar Skylot

core: don't inline variables defined in 'try' and used in 'catch'

parent 58998089
......@@ -57,13 +57,13 @@ public class Jadx {
passes.add(new SSATransform());
passes.add(new DebugInfoVisitor());
passes.add(new TypeInference());
if (args.isRawCFGOutput()) {
passes.add(new DotGraphVisitor(outDir, false, true));
}
passes.add(new ConstInlinerVisitor());
passes.add(new FinishTypeInference());
passes.add(new EliminatePhiNodes());
if (args.isRawCFGOutput()) {
passes.add(new DotGraphVisitor(outDir, false, true));
}
passes.add(new ModVisitor());
passes.add(new EnumVisitor());
......
......@@ -442,6 +442,10 @@ public class MethodNode extends LineAttrNode implements ILoadable {
return exceptionHandlers.isEmpty();
}
public int getExceptionHandlersCount() {
return exceptionHandlers.size();
}
/**
* Return true if exists method with same name and arguments count
*/
......
......@@ -8,9 +8,11 @@ import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.exceptions.JadxException;
......@@ -24,35 +26,52 @@ public class ConstInlinerVisitor extends AbstractVisitor {
if (mth.isNoCode()) {
return;
}
List<InsnNode> toRemove = new ArrayList<InsnNode>();
for (BlockNode block : mth.getBasicBlocks()) {
InstructionRemover remover = new InstructionRemover(mth, block);
toRemove.clear();
for (InsnNode insn : block.getInstructions()) {
if (checkInsn(mth, block, insn)) {
remover.add(insn);
toRemove.add(insn);
}
}
remover.perform();
if (!toRemove.isEmpty()) {
InstructionRemover.removeAll(mth, block, toRemove);
}
}
}
private static boolean checkInsn(MethodNode mth, BlockNode block, InsnNode insn) {
if (insn.getType() == InsnType.CONST) {
InsnArg arg = insn.getArg(0);
if (arg.isLiteral()) {
ArgType resType = insn.getResult().getType();
// make sure arg has correct type
if (!arg.getType().isTypeKnown()) {
arg.merge(resType);
if (insn.getType() != InsnType.CONST) {
return false;
}
InsnArg arg = insn.getArg(0);
if (!arg.isLiteral()) {
return false;
}
SSAVar sVar = insn.getResult().getSVar();
if (mth.getExceptionHandlersCount() != 0) {
for (RegisterArg useArg : sVar.getUseList()) {
InsnNode parentInsn = useArg.getParentInsn();
if (parentInsn != null) {
// TODO: speed up expensive operations
BlockNode useBlock = BlockUtils.getBlockByInsn(mth, parentInsn);
if (!BlockUtils.isCleanPathExists(block, useBlock)) {
return false;
}
}
long lit = ((LiteralArg) arg).getLiteral();
return replaceConst(mth, insn, lit);
}
}
return false;
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, sVar, lit);
}
private static boolean replaceConst(MethodNode mth, InsnNode insn, long literal) {
List<RegisterArg> use = new ArrayList<RegisterArg>(insn.getResult().getSVar().getUseList());
private static boolean replaceConst(MethodNode mth, SSAVar sVar, long literal) {
List<RegisterArg> use = new ArrayList<RegisterArg>(sVar.getUseList());
int replaceCount = 0;
for (RegisterArg arg : use) {
// if (arg.getSVar().isUsedInPhi()) {
......
......@@ -216,6 +216,29 @@ public class BlockUtils {
return traverseSuccessorsUntil(start, end, new BitSet());
}
public static boolean isCleanPathExists(BlockNode start, BlockNode end) {
if (start == end || start.getCleanSuccessors().contains(end)) {
return true;
}
return traverseCleanSuccessorsUntil(start, end, new BitSet());
}
private static boolean traverseCleanSuccessorsUntil(BlockNode from, BlockNode until, BitSet visited) {
for (BlockNode s : from.getCleanSuccessors()) {
if (s == until) {
return true;
}
int id = s.getId();
if (!visited.get(id) && !s.contains(AType.EXC_HANDLER)) {
visited.set(id);
if (traverseSuccessorsUntil(s, until, visited)) {
return true;
}
}
}
return false;
}
public static boolean isOnlyOnePathExists(BlockNode start, BlockNode end) {
if (start == end) {
return true;
......
......@@ -47,7 +47,7 @@ public class InstructionRemover {
if (toRemove.isEmpty()) {
return;
}
removeAll(insns, toRemove);
removeAll(mth, insns, toRemove);
toRemove.clear();
}
......@@ -86,7 +86,7 @@ public class InstructionRemover {
// Don't use 'insns.removeAll(toRemove)' because it will remove instructions by content
// and here can be several instructions with same content
private void removeAll(List<InsnNode> insns, List<InsnNode> toRemove) {
private static void removeAll(MethodNode mth, List<InsnNode> insns, List<InsnNode> toRemove) {
for (InsnNode rem : toRemove) {
unbindInsn(mth, rem);
int insnsCount = insns.size();
......@@ -119,6 +119,10 @@ public class InstructionRemover {
}
}
public static void removeAll(MethodNode mth, BlockNode block, List<InsnNode> insns) {
removeAll(mth, block.getInstructions(), insns);
}
public static void remove(MethodNode mth, BlockNode block, int index) {
List<InsnNode> instructions = block.getInstructions();
unbindInsn(mth, instructions.get(index));
......
......@@ -29,6 +29,7 @@ import static org.junit.Assert.fail;
public abstract class InternalJadxTest extends TestUtils {
protected boolean outputCFG = false;
protected boolean isFallback = false;
protected boolean deleteTmpJar = true;
protected String outDir = "test-out-tmp";
......@@ -76,6 +77,11 @@ public abstract class InternalJadxTest extends TestUtils {
public boolean isRawCFGOutput() {
return outputCFG;
}
@Override
public boolean isFallbackMode() {
return isFallback;
}
}, new File(outDir));
}
......@@ -138,6 +144,12 @@ public abstract class InternalJadxTest extends TestUtils {
// Use only for debug purpose
@Deprecated
protected void setFallback() {
this.isFallback = true;
}
// Use only for debug purpose
@Deprecated
protected void notDeleteTmpJar() {
this.deleteTmpJar = false;
}
......
......@@ -41,7 +41,6 @@ public class TestIfInTry extends InternalJadxTest {
@Test
public void test() {
setOutputCFG();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
......
package jadx.tests.internal.trycatch;
import jadx.api.InternalJadxTest;
import jadx.core.dex.nodes.ClassNode;
import java.io.File;
import org.junit.Test;
import static jadx.tests.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertThat;
public class TestInlineInCatch extends InternalJadxTest {
public static class TestCls {
private File dir;
public int test() {
File output = null;
try {
output = File.createTempFile("f", "a", dir);
return 0;
} catch (Exception e) {
if (output != null) {
output.delete();
}
return 2;
}
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsOne("File output = null;"));
assertThat(code, containsOne("output = File.createTempFile(\"f\", \"a\", "));
assertThat(code, containsOne("return 0;"));
assertThat(code, containsOne("} catch (Exception e) {"));
assertThat(code, containsOne("if (output != null) {"));
assertThat(code, containsOne("output.delete();"));
assertThat(code, containsOne("return 2;"));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment