Commit d905c96f authored by Skylot's avatar Skylot

core: refactor 'catch' clause variable processing

parent 03f03f85
......@@ -292,7 +292,7 @@ public class CodeWriter {
out = new PrintWriter(file, "UTF-8");
String code = buf.toString();
code = removeFirstEmptyLine(code);
out.print(code);
out.println(code);
} catch (Exception e) {
LOG.error("Save file error", e);
} finally {
......
......@@ -436,14 +436,6 @@ public class InsnGen {
}
break;
case MOVE_EXCEPTION:
if (isFallback()) {
code.add("move-exception");
} else {
addArg(code, insn.getArg(0));
}
break;
case TERNARY:
makeTernary((TernaryInsn) insn, code, state);
break;
......@@ -472,6 +464,11 @@ public class InsnGen {
code.add("goto ").add(MethodGen.getLabelName(((GotoNode) insn).getTarget()));
break;
case MOVE_EXCEPTION:
assert isFallback();
code.add("move-exception");
break;
case SWITCH:
assert isFallback();
SwitchNode sw = (SwitchNode) insn;
......
......@@ -9,6 +9,7 @@ import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.SwitchNode;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.NamedArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.FieldNode;
......@@ -19,13 +20,13 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.SwitchRegion;
import jadx.core.dex.regions.SynchronizedRegion;
import jadx.core.dex.regions.TryCatchRegion;
import jadx.core.dex.regions.conditions.IfCondition;
import jadx.core.dex.regions.conditions.IfRegion;
import jadx.core.dex.regions.loops.ForEachLoop;
import jadx.core.dex.regions.loops.ForLoop;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.regions.loops.LoopType;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.ErrorsCounter;
......@@ -59,6 +60,8 @@ public class RegionGen extends InsnGen {
makeSwitch((SwitchRegion) cont, code);
} else if (cont instanceof LoopRegion) {
makeLoop((LoopRegion) cont, code);
} else if (cont instanceof TryCatchRegion) {
makeTryCatch((TryCatchRegion) cont, code);
} else if (cont instanceof SynchronizedRegion) {
makeSynchronizedRegion((SynchronizedRegion) cont, code);
}
......@@ -80,14 +83,9 @@ public class RegionGen extends InsnGen {
}
private void makeSimpleRegion(CodeWriter code, Region region) throws CodegenException {
CatchAttr tc = region.get(AType.CATCH_BLOCK);
if (tc != null) {
makeTryCatch(region, tc.getTryBlock(), code);
} else {
declareVars(code, region);
for (IContainer c : region.getSubBlocks()) {
makeRegion(code, c);
}
declareVars(code, region);
for (IContainer c : region.getSubBlocks()) {
makeRegion(code, c);
}
}
......@@ -283,11 +281,11 @@ public class RegionGen extends InsnGen {
}
}
private void makeTryCatch(IContainer region, TryCatchBlock tryCatchBlock, CodeWriter code)
throws CodegenException {
private void makeTryCatch(TryCatchRegion region, CodeWriter code) throws CodegenException {
TryCatchBlock tryCatchBlock = region.geTryCatchBlock();
code.startLine("try {");
region.remove(AType.CATCH_BLOCK);
makeRegionIndent(code, region);
makeRegionIndent(code, region.getTryRegion());
// TODO: move search of 'allHandler' to 'TryCatchRegion'
ExceptionHandler allHandler = null;
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
if (!handler.isCatchAll()) {
......@@ -309,20 +307,25 @@ public class RegionGen extends InsnGen {
code.startLine('}');
}
private void makeCatchBlock(CodeWriter code, ExceptionHandler handler)
throws CodegenException {
private void makeCatchBlock(CodeWriter code, ExceptionHandler handler) throws CodegenException {
IContainer region = handler.getHandlerRegion();
if (region != null) {
code.startLine("} catch (");
if (region == null) {
return;
}
code.startLine("} catch (");
InsnArg arg = handler.getArg();
if (arg instanceof RegisterArg) {
declareVar(code, (RegisterArg) arg);
} else if (arg instanceof NamedArg) {
if (handler.isCatchAll()) {
code.add("Throwable");
} else {
useClass(code, handler.getCatchType());
}
code.add(' ');
code.add(mgen.getNameGen().assignNamedArg(handler.getArg()));
code.add(") {");
makeRegionIndent(code, region);
code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));
}
code.add(") {");
makeRegionIndent(code, region);
}
}
......@@ -4,7 +4,6 @@ import jadx.core.dex.info.FieldInfo;
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.NamedArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.DexNode;
......@@ -404,8 +403,7 @@ public class InsnDecoder {
case Opcodes.MOVE_EXCEPTION:
return insn(InsnType.MOVE_EXCEPTION,
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)),
new NamedArg("e", ArgType.unknown(PrimitiveType.OBJECT)));
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)));
case Opcodes.RETURN_VOID:
return new InsnNode(InsnType.RETURN, 0);
......
......@@ -30,8 +30,8 @@ public abstract class InsnArg extends Typed {
return reg(InsnUtils.getArg(insn, argNum), type);
}
public static MthParameterArg parameterReg(int regNum, ArgType type) {
return new MthParameterArg(regNum, type);
public static TypeImmutableArg typeImmutableReg(int regNum, ArgType type) {
return new TypeImmutableArg(regNum, type);
}
public static LiteralArg lit(long literal, ArgType type) {
......
package jadx.core.dex.instructions.args;
public class MthParameterArg extends RegisterArg {
public class TypeImmutableArg extends RegisterArg {
private boolean isThis;
public MthParameterArg(int rn, ArgType type) {
public TypeImmutableArg(int rn, ArgType type) {
super(rn, type);
}
......@@ -48,13 +48,13 @@ public class MthParameterArg extends RegisterArg {
if (this == obj) {
return true;
}
if (!(obj instanceof MthParameterArg)) {
if (!(obj instanceof TypeImmutableArg)) {
return false;
}
if (!super.equals(obj)) {
return false;
}
MthParameterArg that = (MthParameterArg) obj;
TypeImmutableArg that = (TypeImmutableArg) obj;
return isThis == that.isThis;
}
......
......@@ -4,6 +4,8 @@ import jadx.core.dex.attributes.IAttributeNode;
public interface IContainer extends IAttributeNode {
// unique id for use in 'toString()' method
/**
* Unique id for use in 'toString()' method
*/
String baseString();
}
......@@ -170,7 +170,6 @@ public class InsnNode extends LineAttrNode {
case NEW_ARRAY:
case NEW_MULTIDIM_ARRAY:
case STR_CONCAT:
case MOVE_EXCEPTION:
return true;
default:
......
......@@ -15,9 +15,9 @@ import jadx.core.dex.instructions.InsnDecoder;
import jadx.core.dex.instructions.SwitchNode;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.MthParameterArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.instructions.args.TypeImmutableArg;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.regions.Region;
import jadx.core.dex.trycatch.ExcHandlerAttr;
......@@ -183,7 +183,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
if (accFlags.isStatic()) {
thisArg = null;
} else {
MthParameterArg arg = InsnArg.parameterReg(pos - 1, parentClass.getClassInfo().getType());
TypeImmutableArg arg = InsnArg.typeImmutableReg(pos - 1, parentClass.getClassInfo().getType());
arg.markAsThis();
thisArg = arg;
}
......@@ -193,7 +193,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
}
argsList = new ArrayList<RegisterArg>(args.size());
for (ArgType arg : args) {
argsList.add(InsnArg.parameterReg(pos, arg));
argsList.add(InsnArg.typeImmutableReg(pos, arg));
pos += arg.getRegCount();
}
}
......
package jadx.core.dex.regions;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.utils.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public final class TryCatchRegion extends AbstractRegion {
private final IContainer tryRegion;
private List<IContainer> catchRegions = Collections.emptyList();
private TryCatchBlock tryCatchBlock;
public TryCatchRegion(IRegion parent, IContainer tryRegion) {
super(parent);
this.tryRegion = tryRegion;
}
public IContainer getTryRegion() {
return tryRegion;
}
public List<IContainer> getCatchRegions() {
return catchRegions;
}
public TryCatchBlock geTryCatchBlock() {
return tryCatchBlock;
}
public void setTryCatchBlock(TryCatchBlock tryCatchBlock) {
this.tryCatchBlock = tryCatchBlock;
this.catchRegions = new ArrayList<IContainer>(tryCatchBlock.getHandlersCount());
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
catchRegions.add(handler.getHandlerRegion());
}
}
@Override
public List<IContainer> getSubBlocks() {
List<IContainer> all = new ArrayList<IContainer>(1 + catchRegions.size());
all.add(tryRegion);
all.addAll(catchRegions);
return Collections.unmodifiableList(all);
}
@Override
public String baseString() {
return tryRegion.baseString();
}
@Override
public String toString() {
return "Try: " + tryRegion
+ " catches: " + Utils.listToString(catchRegions);
}
}
......@@ -2,7 +2,7 @@ package jadx.core.dex.trycatch;
import jadx.core.Consts;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.instructions.args.NamedArg;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IContainer;
import jadx.core.utils.InsnUtils;
......@@ -18,7 +18,7 @@ public class ExceptionHandler {
private BlockNode handlerBlock;
private final List<BlockNode> blocks = new ArrayList<BlockNode>();
private IContainer handlerRegion;
private NamedArg arg;
private InsnArg arg;
private TryCatchBlock tryBlock;
......@@ -63,11 +63,11 @@ public class ExceptionHandler {
this.handlerRegion = handlerRegion;
}
public NamedArg getArg() {
public InsnArg getArg() {
return arg;
}
public void setArg(NamedArg arg) {
public void setArg(InsnArg arg) {
this.arg = arg;
}
......
......@@ -4,7 +4,7 @@ import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.NamedArg;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
......@@ -43,30 +43,24 @@ public class BlockProcessingHelper {
* Set exception handler attribute for whole block
*/
private static void markExceptionHandlers(BlockNode block) {
if (!block.getInstructions().isEmpty()) {
InsnNode me = block.getInstructions().get(0);
ExcHandlerAttr handlerAttr = me.get(AType.EXC_HANDLER);
if (handlerAttr != null && me.getType() == InsnType.MOVE_EXCEPTION) {
ExceptionHandler excHandler = handlerAttr.getHandler();
assert me.getOffset() == excHandler.getHandleOffset();
// set correct type for 'move-exception' operation
RegisterArg resArg = me.getResult();
NamedArg excArg = (NamedArg) me.getArg(0);
ArgType type;
if (excHandler.isCatchAll()) {
type = ArgType.THROWABLE;
excArg.setName("th");
} else {
type = excHandler.getCatchType().getType();
excArg.setName("e");
}
resArg.forceType(type);
excArg.setType(type);
excHandler.setArg(excArg);
block.addAttr(handlerAttr);
}
if (block.getInstructions().isEmpty()) {
return;
}
InsnNode me = block.getInstructions().get(0);
ExcHandlerAttr handlerAttr = me.get(AType.EXC_HANDLER);
if (handlerAttr == null || me.getType() != InsnType.MOVE_EXCEPTION) {
return;
}
ExceptionHandler excHandler = handlerAttr.getHandler();
block.addAttr(handlerAttr);
// set correct type for 'move-exception' operation
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
RegisterArg resArg = me.getResult();
resArg = InsnArg.reg(resArg.getRegNum(), type);
me.setResult(resArg);
excHandler.setArg(resArg);
}
private static void processExceptionHandlers(MethodNode mth, BlockNode block) {
......
......@@ -200,7 +200,9 @@ public class CodeShrinker extends AbstractVisitor {
continue;
}
InsnNode assignInsn = sVar.getAssign().getParentInsn();
if (assignInsn == null || assignInsn instanceof PhiInsn) {
if (assignInsn == null
|| assignInsn instanceof PhiInsn
|| assignInsn.getType() == InsnType.MOVE_EXCEPTION) {
continue;
}
int assignPos = insnList.getIndex(assignInsn);
......
......@@ -14,7 +14,9 @@ import jadx.core.dex.instructions.SwitchNode;
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.NamedArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.ClassNode;
......@@ -298,9 +300,27 @@ public class ModVisitor extends AbstractVisitor {
List<InsnNode> blockInsns = block.getInstructions();
if (!blockInsns.isEmpty()) {
InsnNode insn = blockInsns.get(0);
if (insn.getType() == InsnType.MOVE_EXCEPTION
&& insn.getResult().getSVar().getUseCount() == 0) {
InstructionRemover.remove(mth, block, 0);
if (insn.getType() == InsnType.MOVE_EXCEPTION) {
// result arg used both in this insn and exception handler,
RegisterArg resArg = insn.getResult();
ArgType type = excHandler.isCatchAll() ? ArgType.THROWABLE : excHandler.getCatchType().getType();
String name = excHandler.isCatchAll() ? "th" : "e";
if (resArg.getName() == null) {
resArg.setName(name);
}
SSAVar sVar = insn.getResult().getSVar();
if (sVar.getUseCount() == 0) {
excHandler.setArg(new NamedArg(name, type));
InstructionRemover.remove(mth, block, 0);
} else if (sVar.isUsedInPhi()) {
// exception var moved to external variable => replace with 'move' insn
InsnNode moveInsn = new InsnNode(InsnType.MOVE, 1);
moveInsn.setResult(insn.getResult());
NamedArg namedArg = new NamedArg(name, type);
moveInsn.addArg(namedArg);
excHandler.setArg(namedArg);
replaceInsn(block, 0, moveInsn);
}
}
}
int totalSize = 0;
......
......@@ -45,6 +45,7 @@ public class PrepareForCodeGen extends AbstractVisitor {
case NOP:
case MONITOR_ENTER:
case MONITOR_EXIT:
case MOVE_EXCEPTION:
it.remove();
break;
......
......@@ -7,6 +7,7 @@ import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.AbstractRegion;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.TryCatchRegion;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
......@@ -129,33 +130,36 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor {
region = loop.getBody();
}
Region newRegion = new Region(region);
Region tryRegion = new Region(region);
List<IContainer> subBlocks = region.getSubBlocks();
for (IContainer cont : subBlocks) {
if (RegionUtils.isDominatedBy(dominator, cont)) {
if (isHandlerPath(tb, cont)) {
break;
}
newRegion.getSubBlocks().add(cont);
tryRegion.getSubBlocks().add(cont);
}
}
if (newRegion.getSubBlocks().isEmpty()) {
if (tryRegion.getSubBlocks().isEmpty()) {
return false;
}
TryCatchRegion tryCatchRegion = new TryCatchRegion(region, tryRegion);
tryRegion.setParent(tryCatchRegion);
tryCatchRegion.setTryCatchBlock(tb.getCatchAttr().getTryBlock());
// replace first node by region
IContainer firstNode = newRegion.getSubBlocks().get(0);
if (!region.replaceSubBlock(firstNode, newRegion)) {
IContainer firstNode = tryRegion.getSubBlocks().get(0);
if (!region.replaceSubBlock(firstNode, tryCatchRegion)) {
return false;
}
subBlocks.removeAll(newRegion.getSubBlocks());
newRegion.addAttr(tb.getCatchAttr());
subBlocks.removeAll(tryRegion.getSubBlocks());
// fix parents
for (IContainer cont : newRegion.getSubBlocks()) {
// fix parents for tryRegion sub blocks
for (IContainer cont : tryRegion.getSubBlocks()) {
if (cont instanceof AbstractRegion) {
AbstractRegion aReg = (AbstractRegion) cont;
aReg.setParent(newRegion);
aReg.setParent(tryRegion);
}
}
return true;
......
......@@ -4,6 +4,7 @@ import jadx.core.codegen.NameGen;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.VarName;
......@@ -158,6 +159,22 @@ public class ProcessVariables extends AbstractVisitor {
usageMap.remove(new Variable(arg));
}
Iterator<Entry<Variable, Usage>> umIt = usageMap.entrySet().iterator();
while (umIt.hasNext()) {
Entry<Variable, Usage> entry = umIt.next();
Usage u = entry.getValue();
// if no assigns => remove
if (u.getAssigns().isEmpty()) {
umIt.remove();
continue;
}
// variable declared at 'catch' clause
InsnNode parentInsn = u.getArg().getParentInsn();
if (parentInsn == null || parentInsn.getType() == InsnType.MOVE_EXCEPTION) {
umIt.remove();
}
}
if (usageMap.isEmpty()) {
return;
}
......@@ -168,13 +185,6 @@ public class ProcessVariables extends AbstractVisitor {
for (Iterator<Entry<Variable, Usage>> it = usageMap.entrySet().iterator(); it.hasNext(); ) {
Entry<Variable, Usage> entry = it.next();
Usage u = entry.getValue();
// if no assigns => remove
if (u.getAssigns().isEmpty()) {
it.remove();
continue;
}
// check if variable can be declared at current assigns
for (IRegion assignRegion : u.getAssigns()) {
if (u.getArgRegion() == assignRegion
......@@ -186,6 +196,9 @@ public class ProcessVariables extends AbstractVisitor {
}
}
}
if (usageMap.isEmpty()) {
return;
}
// apply
for (Entry<Variable, Usage> entry : usageMap.entrySet()) {
......@@ -262,7 +275,7 @@ public class ProcessVariables extends AbstractVisitor {
}
private static int calculateOrder(IContainer container, Map<IContainer, Integer> regionsOrder,
int id, boolean inc) {
int id, boolean inc) {
if (!(container instanceof IRegion)) {
return id;
}
......@@ -299,8 +312,8 @@ public class ProcessVariables extends AbstractVisitor {
&& isAllRegionsAfter(region, pos, u.getUseRegions(), regionsOrder);
}
private static boolean isAllRegionsAfter(IRegion region, Integer pos,
Set<IRegion> regions, Map<IContainer, Integer> regionsOrder) {
private static boolean isAllRegionsAfter(IRegion region, int pos,
Set<IRegion> regions, Map<IContainer, Integer> regionsOrder) {
for (IRegion r : regions) {
if (r == region) {
continue;
......@@ -313,7 +326,7 @@ public class ProcessVariables extends AbstractVisitor {
if (pos > rPos) {
return false;
}
if (pos.equals(rPos)) {
if (pos == rPos) {
return isAllRegionsAfterRecursive(region, regions);
}
}
......
......@@ -23,10 +23,11 @@ public class TestArgInline extends IntegrationTest {
@Test
public void test() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsString("a++;"));
assertThat(code, not(containsString("a = a + 1;")));
assertThat(code, containsString("i++;"));
assertThat(code, not(containsString("i = i + 1;")));
}
}
......@@ -12,7 +12,6 @@ import static org.junit.Assert.assertThat;
public class TestIfTryInCatch extends IntegrationTest {
public static class TestCls {
private static final String TAG = "TAG";
private Exception exception;
private java.lang.Object data;
......@@ -35,11 +34,11 @@ public class TestIfTryInCatch extends IntegrationTest {
}
private static boolean b(Object obj) {
return false;
return obj == null;
}
private static boolean a(Exception e) {
return false;
return e instanceof RuntimeException;
}
private Object f() {
......
package jadx.tests.integration.trycatch;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertThat;
public class TestTryCatch7 extends IntegrationTest {
public static class TestCls {
private Exception test() {
Exception e = new Exception();
try {
Thread.sleep(50);
} catch (Exception ex) {
e = ex;
}
e.printStackTrace();
return e;
}
}
@Test
public void test() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
String excVarName = "exception";
assertThat(code, containsOne("Exception " + excVarName + " = new Exception();"));
assertThat(code, containsOne("} catch (Exception e) {"));
assertThat(code, containsOne(excVarName + " = e;"));
assertThat(code, containsOne(excVarName + ".printStackTrace();"));
assertThat(code, containsOne("return " + excVarName + ";"));
}
}
......@@ -67,4 +67,10 @@ public class TestVariables4 extends IntegrationTest {
assertThat(code, containsString("System.err.println(\"Class '\" + clsName + \"' not found\");"));
assertThat(code, containsString("return pass;"));
}
@Test
public void test2() {
noDebugInfo();
getClassNode(TestCls.class);
}
}
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