Commit 0a36bfb0 authored by Skylot's avatar Skylot

core: fix try-catch blocks processing

parent 0d94af09
...@@ -68,10 +68,6 @@ public class Jadx { ...@@ -68,10 +68,6 @@ public class Jadx {
passes.add(new ModVisitor()); passes.add(new ModVisitor());
passes.add(new EnumVisitor()); passes.add(new EnumVisitor());
if (args.isCFGOutput()) {
passes.add(new DotGraphVisitor(outDir, false));
}
passes.add(new CodeShrinker()); passes.add(new CodeShrinker());
passes.add(new RegionMakerVisitor()); passes.add(new RegionMakerVisitor());
passes.add(new IfRegionVisitor()); passes.add(new IfRegionVisitor());
...@@ -89,6 +85,10 @@ public class Jadx { ...@@ -89,6 +85,10 @@ public class Jadx {
passes.add(new MethodInlineVisitor()); passes.add(new MethodInlineVisitor());
passes.add(new ClassModifier()); passes.add(new ClassModifier());
passes.add(new PrepareForCodeGen()); passes.add(new PrepareForCodeGen());
if (args.isCFGOutput()) {
passes.add(new DotGraphVisitor(outDir, false));
}
} }
passes.add(new CodeGen(args)); passes.add(new CodeGen(args));
return passes; return passes;
......
...@@ -3,7 +3,6 @@ package jadx.core; ...@@ -3,7 +3,6 @@ package jadx.core;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.DecodeException;
import java.util.List; import java.util.List;
...@@ -22,8 +21,6 @@ public final class ProcessClass { ...@@ -22,8 +21,6 @@ public final class ProcessClass {
for (IDexTreeVisitor visitor : passes) { for (IDexTreeVisitor visitor : passes) {
DepthTraversal.visit(visitor, cls); DepthTraversal.visit(visitor, cls);
} }
} catch (DecodeException e) {
LOG.error("Decode exception: " + cls, e);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Class process exception: " + cls, e); LOG.error("Class process exception: " + cls, e);
} finally { } finally {
......
...@@ -66,6 +66,13 @@ public class RegisterArg extends InsnArg implements Named { ...@@ -66,6 +66,13 @@ public class RegisterArg extends InsnArg implements Named {
return name; return name;
} }
public boolean isNameEquals(InsnArg arg) {
if (name == null || !(arg instanceof Named)) {
return false;
}
return name.equals(((Named) arg).getName());
}
public void setName(String newName) { public void setName(String newName) {
this.name = newName; this.name = newName;
} }
......
...@@ -47,6 +47,10 @@ public class SSAVar { ...@@ -47,6 +47,10 @@ public class SSAVar {
return useList; return useList;
} }
public int getUseCount() {
return useList.size();
}
public void use(RegisterArg arg) { public void use(RegisterArg arg) {
mergeName(arg); mergeName(arg);
if (arg.getSVar() != null) { if (arg.getSVar() != null) {
...@@ -81,7 +85,7 @@ public class SSAVar { ...@@ -81,7 +85,7 @@ public class SSAVar {
if (!isUsedInPhi()) { if (!isUsedInPhi()) {
return useList.size(); return useList.size();
} }
return useList.size() + usedInPhi.getResult().getSVar().getUseList().size(); return useList.size() + usedInPhi.getResult().getSVar().getUseCount();
} }
public ArgType getType() { public ArgType getType() {
......
...@@ -205,7 +205,7 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -205,7 +205,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
} }
@Override @Override
public void load() throws DecodeException { public void load() {
for (MethodNode mth : getMethods()) { for (MethodNode mth : getMethods()) {
try { try {
mth.load(); mth.load();
......
...@@ -446,8 +446,12 @@ public class MethodNode extends LineAttrNode implements ILoadable { ...@@ -446,8 +446,12 @@ public class MethodNode extends LineAttrNode implements ILoadable {
return handler; return handler;
} }
public List<ExceptionHandler> getExceptionHandlers() { public Iterable<ExceptionHandler> getExceptionHandlers() {
return Collections.unmodifiableList(exceptionHandlers); return exceptionHandlers;
}
public boolean isNoExceptionHandlers() {
return exceptionHandlers.isEmpty();
} }
/** /**
......
...@@ -79,7 +79,7 @@ public final class LoopRegion extends AbstractRegion { ...@@ -79,7 +79,7 @@ public final class LoopRegion extends AbstractRegion {
return false; return false;
} else { } else {
RegisterArg res = insn.getResult(); RegisterArg res = insn.getResult();
if (res.getSVar().getUseList().size() > 1) { if (res.getSVar().getUseCount() > 1) {
return false; return false;
} }
boolean found = false; boolean found = false;
......
...@@ -99,11 +99,10 @@ public class ClassModifier extends AbstractVisitor { ...@@ -99,11 +99,10 @@ public class ClassModifier extends AbstractVisitor {
mth.removeFirstArgument(); mth.removeFirstArgument();
InstructionRemover.remove(mth, block, insn); InstructionRemover.remove(mth, block, insn);
// other arg usage -> wrap with IGET insn // other arg usage -> wrap with IGET insn
List<RegisterArg> useList = arg.getSVar().getUseList(); if (arg.getSVar().getUseCount() != 0) {
if (useList.size() > 0) {
InsnNode iget = new IndexInsnNode(InsnType.IGET, fieldInfo, 1); InsnNode iget = new IndexInsnNode(InsnType.IGET, fieldInfo, 1);
iget.addArg(insn.getArg(1)); iget.addArg(insn.getArg(1));
for (InsnArg insnArg : useList) { for (InsnArg insnArg : arg.getSVar().getUseList()) {
insnArg.wrapInstruction(iget); insnArg.wrapInstruction(iget);
} }
} }
......
...@@ -229,7 +229,7 @@ public class ModVisitor extends AbstractVisitor { ...@@ -229,7 +229,7 @@ public class ModVisitor extends AbstractVisitor {
if (blockInsns.size() > 0) { if (blockInsns.size() > 0) {
InsnNode insn = blockInsns.get(0); InsnNode insn = blockInsns.get(0);
if (insn.getType() == InsnType.MOVE_EXCEPTION if (insn.getType() == InsnType.MOVE_EXCEPTION
&& insn.getResult().getSVar().getUseList().isEmpty()) { && insn.getResult().getSVar().getUseCount() == 0) {
InstructionRemover.remove(mth, block, 0); InstructionRemover.remove(mth, block, 0);
} }
} }
...@@ -257,7 +257,7 @@ public class ModVisitor extends AbstractVisitor { ...@@ -257,7 +257,7 @@ public class ModVisitor extends AbstractVisitor {
* In argument not used in other instructions then remove assign instruction. * In argument not used in other instructions then remove assign instruction.
*/ */
private static void removeInsnForArg(InstructionRemover remover, RegisterArg arg) { private static void removeInsnForArg(InstructionRemover remover, RegisterArg arg) {
if (arg.getSVar().getUseList().isEmpty() if (arg.getSVar().getUseCount() == 0
&& arg.getAssignInsn() != null) { && arg.getAssignInsn() != null) {
remover.add(arg.getAssignInsn()); remover.add(arg.getAssignInsn());
} }
......
...@@ -54,6 +54,16 @@ public class PrepareForCodeGen extends AbstractVisitor { ...@@ -54,6 +54,16 @@ public class PrepareForCodeGen extends AbstractVisitor {
it.remove(); it.remove();
} }
break; break;
case MOVE:
// remove redundant moves:
// unused result and same args names (a = a;)
RegisterArg result = insn.getResult();
if (result.getSVar().getUseCount() == 0
&& result.isNameEquals(insn.getArg(0))) {
it.remove();
}
break;
} }
} }
} }
......
...@@ -40,7 +40,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { ...@@ -40,7 +40,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
private final Map<BlockNode, TryCatchBlock> tryBlocksMap = new HashMap<BlockNode, TryCatchBlock>(2); private final Map<BlockNode, TryCatchBlock> tryBlocksMap = new HashMap<BlockNode, TryCatchBlock>(2);
public ProcessTryCatchRegions(MethodNode mth) { public ProcessTryCatchRegions(MethodNode mth) {
if (mth.isNoCode() || mth.getExceptionHandlers().isEmpty()) { if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
return; return;
} }
...@@ -84,7 +84,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { ...@@ -84,7 +84,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb); TryCatchBlock prevTB = tryBlocksMap.put(domBlock, tb);
if (prevTB != null) { if (prevTB != null) {
LOG.info("!!! TODO merge try blocks in " + mth); LOG.info("!!! TODO: merge try blocks in " + mth);
} }
} }
...@@ -134,7 +134,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { ...@@ -134,7 +134,7 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
} }
} }
} }
if (newRegion.getSubBlocks().size() != 0) { if (!newRegion.getSubBlocks().isEmpty()) {
if (DEBUG) { if (DEBUG) {
LOG.debug("ProcessTryCatchRegions mark: {}", newRegion); LOG.debug("ProcessTryCatchRegions mark: {}", newRegion);
} }
......
...@@ -685,11 +685,6 @@ public class RegionMaker { ...@@ -685,11 +685,6 @@ public class RegionMaker {
LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler)); LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler));
return; return;
} }
BlockNode out = BlockUtils.traverseWhileDominates(start, start);
if (out != null) {
stack.addExit(out);
}
// TODO extract finally part which exists in all handlers from same try block // TODO extract finally part which exists in all handlers from same try block
// TODO add blocks common for several handlers to some region // TODO add blocks common for several handlers to some region
handler.setHandlerRegion(makeRegion(start, stack)); handler.setHandlerRegion(makeRegion(start, stack));
......
...@@ -34,7 +34,7 @@ public class RegionMakerVisitor extends AbstractVisitor { ...@@ -34,7 +34,7 @@ public class RegionMakerVisitor extends AbstractVisitor {
// fill region structure // fill region structure
mth.setRegion(rm.makeRegion(mth.getEnterBlock(), state)); mth.setRegion(rm.makeRegion(mth.getEnterBlock(), state));
if (!mth.getExceptionHandlers().isEmpty()) { if (!mth.isNoExceptionHandlers()) {
state = new RegionStack(mth); state = new RegionStack(mth);
for (ExceptionHandler handler : mth.getExceptionHandlers()) { for (ExceptionHandler handler : mth.getExceptionHandlers()) {
rm.processExcHandler(handler, state); rm.processExcHandler(handler, state);
...@@ -46,8 +46,9 @@ public class RegionMakerVisitor extends AbstractVisitor { ...@@ -46,8 +46,9 @@ public class RegionMakerVisitor extends AbstractVisitor {
private static void postProcessRegions(MethodNode mth) { private static void postProcessRegions(MethodNode mth) {
// make try-catch regions // make try-catch regions
DepthRegionTraversal.traverse(mth, new ProcessTryCatchRegions(mth)); if (!mth.isNoExceptionHandlers()) {
DepthRegionTraversal.traverse(mth, new ProcessTryCatchRegions(mth));
}
// merge conditions in loops // merge conditions in loops
if (mth.getLoopsCount() != 0) { if (mth.getLoopsCount() != 0) {
DepthRegionTraversal.traverseAll(mth, new AbstractRegionVisitor() { DepthRegionTraversal.traverseAll(mth, new AbstractRegionVisitor() {
......
...@@ -146,7 +146,7 @@ public class SSATransform extends AbstractVisitor { ...@@ -146,7 +146,7 @@ public class SSATransform extends AbstractVisitor {
List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>(); List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
for (SSAVar var : mth.getSVars()) { for (SSAVar var : mth.getSVars()) {
// phi result not used // phi result not used
if (var.getUseList().isEmpty()) { if (var.getUseCount() == 0) {
InsnNode assignInsn = var.getAssign().getParentInsn(); InsnNode assignInsn = var.getAssign().getParentInsn();
if (assignInsn != null && assignInsn.getType() == InsnType.PHI) { if (assignInsn != null && assignInsn.getType() == InsnType.PHI) {
insnToRemove.add((PhiInsn) assignInsn); insnToRemove.add((PhiInsn) assignInsn);
......
...@@ -229,14 +229,14 @@ public class BlockUtils { ...@@ -229,14 +229,14 @@ public class BlockUtils {
} }
/** /**
* Search for first node which not dominated by block, starting from child * Search for first node which not dominated by dom, starting from start
*/ */
public static BlockNode traverseWhileDominates(BlockNode block, BlockNode child) { public static BlockNode traverseWhileDominates(BlockNode dom, BlockNode start) {
for (BlockNode node : child.getCleanSuccessors()) { for (BlockNode node : start.getCleanSuccessors()) {
if (!node.isDominator(block)) { if (!node.isDominator(dom)) {
return node; return node;
} else { } else {
BlockNode out = traverseWhileDominates(block, node); BlockNode out = traverseWhileDominates(dom, node);
if (out != null) { if (out != null) {
return out; return out;
} }
......
...@@ -59,7 +59,7 @@ public class InstructionRemover { ...@@ -59,7 +59,7 @@ public class InstructionRemover {
public static void unbindInsn(MethodNode mth, InsnNode insn) { public static void unbindInsn(MethodNode mth, InsnNode insn) {
RegisterArg r = insn.getResult(); RegisterArg r = insn.getResult();
if (r != null && r.getSVar() != null) { if (r != null && r.getSVar() != null) {
if (Consts.DEBUG && !r.getSVar().getUseList().isEmpty()) { if (Consts.DEBUG && r.getSVar().getUseCount() != 0) {
LOG.debug("Unbind insn with result: {}", insn); LOG.debug("Unbind insn with result: {}", insn);
} }
mth.removeSVar(r.getSVar()); mth.removeSVar(r.getSVar());
......
package jadx.api; package jadx.api;
import jadx.core.Jadx; import jadx.core.Jadx;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.DepthTraversal;
...@@ -23,6 +25,7 @@ import static org.hamcrest.CoreMatchers.not; ...@@ -23,6 +25,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public abstract class InternalJadxTest extends TestUtils { public abstract class InternalJadxTest extends TestUtils {
...@@ -33,48 +36,45 @@ public abstract class InternalJadxTest extends TestUtils { ...@@ -33,48 +36,45 @@ public abstract class InternalJadxTest extends TestUtils {
protected String outDir = "test-out-tmp"; protected String outDir = "test-out-tmp";
public ClassNode getClassNode(Class<?> clazz) { public ClassNode getClassNode(Class<?> clazz) {
Decompiler d = new Decompiler();
try { try {
File temp = getJarForClass(clazz); d.loadFile(getJarForClass(clazz));
Decompiler d = new Decompiler(); } catch (Exception e) {
try { fail(e.getMessage());
d.loadFile(temp); }
} catch (Exception e) { String clsName = clazz.getName();
fail(e.getMessage()); ClassNode cls = d.getRoot().searchClassByName(clsName);
} assertNotNull("Class not found: " + clsName, cls);
List<ClassNode> classes = d.getRoot().getClasses(false); assertEquals(cls.getFullName(), clazz.getName());
String clsName = clazz.getName();
ClassNode cls = null; cls.load();
for (ClassNode aClass : classes) { for (IDexTreeVisitor visitor : getPasses()) {
if (aClass.getFullName().equals(clsName)) { DepthTraversal.visit(visitor, cls);
cls = aClass; }
} // don't unload class
}
assertNotNull("Class not found: " + clsName, cls);
assertEquals(cls.getFullName(), clazz.getName()); assertTrue("Inconsistent cls: " + cls,
!cls.contains(AFlag.INCONSISTENT_CODE) && !cls.contains(AType.JADX_ERROR));
for (MethodNode mthNode : cls.getMethods()) {
assertTrue("Inconsistent method: " + mthNode,
!mthNode.contains(AFlag.INCONSISTENT_CODE) && !mthNode.contains(AType.JADX_ERROR));
}
assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
return cls;
}
cls.load(); protected List<IDexTreeVisitor> getPasses() {
List<IDexTreeVisitor> passes = Jadx.getPassesList(new DefaultJadxArgs() { return Jadx.getPassesList(new DefaultJadxArgs() {
@Override @Override
public boolean isCFGOutput() { public boolean isCFGOutput() {
return outputCFG; return outputCFG;
} }
@Override @Override
public boolean isRawCFGOutput() { public boolean isRawCFGOutput() {
return outputCFG; return outputCFG;
}
}, new File(outDir));
for (IDexTreeVisitor visitor : passes) {
DepthTraversal.visit(visitor, cls);
} }
assertThat(cls.getCode().toString(), not(containsString("inconsistent"))); }, new File(outDir));
return cls;
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
return null;
}
} }
protected MethodNode getMethod(ClassNode cls, String method) { protected MethodNode getMethod(ClassNode cls, String method) {
......
...@@ -4,7 +4,6 @@ import jadx.api.InternalJadxTest; ...@@ -4,7 +4,6 @@ import jadx.api.InternalJadxTest;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.DecodeException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
...@@ -30,7 +29,7 @@ public class TestVariablesDefinitions extends InternalJadxTest { ...@@ -30,7 +29,7 @@ public class TestVariablesDefinitions extends InternalJadxTest {
while (iterator.hasNext()) { while (iterator.hasNext()) {
DepthTraversal.visit(iterator.next(), cls); DepthTraversal.visit(iterator.next(), cls);
} }
} catch (DecodeException e) { } catch (Exception e) {
LOG.error("Decode exception: " + cls, e); LOG.error("Decode exception: " + cls, e);
} }
} }
......
package jadx.tests.internal.trycatch;
import jadx.api.InternalJadxTest;
import jadx.core.dex.nodes.ClassNode;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
public class TestTryCatch4 extends InternalJadxTest {
public static class TestCls {
private Object test(Object obj) {
FileOutputStream output = null;
try {
output = new FileOutputStream(new File("f"));
return new Object();
} catch (FileNotFoundException e) {
System.out.println("Exception");
return null;
}
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
System.out.println(code);
assertThat(code, containsString("try {"));
assertThat(code, containsString("output = new FileOutputStream(new File(\"f\"));"));
assertThat(code, containsString("return new Object();"));
assertThat(code, containsString("} catch (FileNotFoundException e) {"));
assertThat(code, containsString("System.out.println(\"Exception\");"));
assertThat(code, containsString("return null;"));
assertThat(code, not(containsString("output = output;")));
}
}
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