Commit d55969bc authored by Skylot's avatar Skylot

core: fix some 'try/catch/finally' cases

parent 99768940
......@@ -264,8 +264,10 @@ public class ClassGen {
try {
addMethod(code, mth);
} catch (Exception e) {
String msg = ErrorsCounter.methodError(mth, "Method generation error", e);
code.startLine("/* " + msg + CodeWriter.NL + Utils.getStackTrace(e) + " */");
code.newLine().add("/*");
code.newLine().add(ErrorsCounter.methodError(mth, "Method generation error", e));
code.newLine().add(Utils.getStackTrace(e));
code.newLine().add("*/");
}
}
}
......
......@@ -510,7 +510,18 @@ public class InsnGen {
case NEW_INSTANCE:
// only fallback - make new instance in constructor invoke
fallbackOnlyInsn(insn);
code.add("new " + insn.getResult().getType());
code.add("new ").add(insn.getResult().getType().toString());
break;
case PHI:
case MERGE:
fallbackOnlyInsn(insn);
code.add(insn.getType().toString()).add("(");
for (InsnArg insnArg : insn.getArguments()) {
addArg(code, insnArg);
code.add(' ');
}
code.add(")");
break;
default:
......
......@@ -167,7 +167,7 @@ public class MethodGen {
if (cause != null) {
code.newLine();
code.add("/*");
code.startLine("Error: ").add(Utils.getStackTrace(cause));
code.newLine().add("Error: ").add(Utils.getStackTrace(cause));
code.add("*/");
}
}
......
......@@ -65,6 +65,9 @@ public enum InsnType {
ONE_ARG,
PHI,
// merge all arguments in one
MERGE,
// TODO: now multidimensional arrays created using Array.newInstance function
NEW_MULTIDIM_ARRAY
}
......@@ -77,8 +77,14 @@ public class RegisterArg extends InsnArg implements Named {
}
public RegisterArg duplicate() {
RegisterArg dup = new RegisterArg(getRegNum(), getType());
dup.setSVar(sVar);
return duplicate(getRegNum(), sVar);
}
public RegisterArg duplicate(int regNum, SSAVar sVar) {
RegisterArg dup = new RegisterArg(regNum, getType());
if (sVar != null) {
dup.setSVar(sVar);
}
dup.copyAttributesFrom(this);
return dup;
}
......
......@@ -554,9 +554,8 @@ public class MethodNode extends LineAttrNode implements ILoadable {
return debugInfoOffset;
}
public SSAVar makeNewSVar(int regNum, int[] versions, @NotNull RegisterArg arg) {
SSAVar var = new SSAVar(regNum, versions[regNum], arg);
versions[regNum]++;
public SSAVar makeNewSVar(int regNum, int version, @NotNull RegisterArg assignArg) {
SSAVar var = new SSAVar(regNum, version, assignArg);
if (sVars.isEmpty()) {
sVars = new ArrayList<SSAVar>();
}
......@@ -564,6 +563,17 @@ public class MethodNode extends LineAttrNode implements ILoadable {
return var;
}
public int getNextSVarVersion(int regNum) {
int v = -1;
for (SSAVar sVar : sVars) {
if (sVar.getRegNum() == regNum) {
v = Math.max(v, sVar.getVersion());
}
}
v++;
return v;
}
public void removeSVar(SSAVar var) {
sVars.remove(var);
}
......
......@@ -103,7 +103,9 @@ public class ConstInlineVisitor extends AbstractVisitor {
int replaceCount = 0;
for (RegisterArg arg : use) {
InsnNode useInsn = arg.getParentInsn();
if (useInsn == null || useInsn.getType() == InsnType.PHI) {
if (useInsn == null
|| useInsn.getType() == InsnType.PHI
|| useInsn.getType() == InsnType.MERGE) {
continue;
}
LiteralArg litArg;
......
......@@ -162,7 +162,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
return false;
}
/* 'finally' extract confirmed, run remove steps */
// 'finally' extract confirmed, run remove steps
LiveVarAnalysis laBefore = null;
boolean runReMap = isReMapNeeded(removes);
......@@ -191,7 +191,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
RegisterArg resArg = me.getResult();
BlockNode succ = handlerBlock.getCleanSuccessors().get(0);
if (laAfter.isLive(succ.getId(), resArg.getRegNum())) {
if (laAfter.isLive(succ, resArg.getRegNum())) {
// kill variable
InsnNode kill = new InsnNode(InsnType.NOP, 0);
kill.setResult(resArg);
......@@ -224,34 +224,59 @@ public class BlockFinallyExtract extends AbstractVisitor {
BitSet processed = new BitSet(mth.getRegsCount());
for (BlocksRemoveInfo removeInfo : removes) {
processed.clear();
BlockNode insertBlock = removeInfo.getStart().getSecond();
BlocksPair start = removeInfo.getStart();
BlockNode insertBlockBefore = start.getFirst();
BlockNode insertBlock = start.getSecond();
if (removeInfo.getRegMap().isEmpty() || insertBlock == null) {
continue;
}
for (Map.Entry<RegisterArg, RegisterArg> entry : removeInfo.getRegMap().entrySet()) {
RegisterArg from = entry.getKey();
int regNum = from.getRegNum();
if (!processed.get(regNum)) {
if (laBefore.isLive(insertBlock.getId(), regNum)) {
RegisterArg fromReg = entry.getKey();
RegisterArg toReg = entry.getValue();
int fromRegNum = fromReg.getRegNum();
int toRegNum = toReg.getRegNum();
if (!processed.get(fromRegNum)) {
boolean liveFromBefore = laBefore.isLive(insertBlockBefore, fromRegNum);
boolean liveFromAfter = laAfter.isLive(insertBlock, fromRegNum);
// boolean liveToBefore = laBefore.isLive(insertBlock, toRegNum);
boolean liveToAfter = laAfter.isLive(insertBlock, toRegNum);
if (liveToAfter && liveFromBefore) {
// merge 'to' and 'from' registers
InsnNode merge = new InsnNode(InsnType.MERGE, 2);
merge.setResult(toReg.duplicate());
merge.addArg(toReg.duplicate());
merge.addArg(fromReg.duplicate());
injectInsn(mth, insertBlock, merge);
} else if (liveFromBefore) {
// remap variable
RegisterArg to = entry.getValue();
InsnNode move = new InsnNode(InsnType.MOVE, 1);
move.setResult(to);
move.addArg(from);
insertBlock.getInstructions().add(move);
} else if (laAfter.isLive(insertBlock.getId(), regNum)) {
move.setResult(toReg.duplicate());
move.addArg(fromReg.duplicate());
injectInsn(mth, insertBlock, move);
} else if (liveFromAfter) {
// kill variable
InsnNode kill = new InsnNode(InsnType.NOP, 0);
kill.setResult(from);
kill.setResult(fromReg.duplicate());
kill.add(AFlag.REMOVE);
insertBlock.getInstructions().add(0, kill);
injectInsn(mth, insertBlock, kill);
}
processed.set(regNum);
processed.set(fromRegNum);
}
}
}
}
private static void injectInsn(MethodNode mth, BlockNode insertBlock, InsnNode insn) {
insn.add(AFlag.SYNTHETIC);
if (insertBlock.getInstructions().isEmpty()) {
insertBlock.getInstructions().add(insn);
} else {
BlockNode succBlock = splitBlock(mth, insertBlock, 0);
BlockNode predBlock = succBlock.getPredecessors().get(0);
predBlock.getInstructions().add(insn);
}
}
private static boolean isReMapNeeded(List<BlocksRemoveInfo> removes) {
for (BlocksRemoveInfo removeInfo : removes) {
if (!removeInfo.getRegMap().isEmpty()) {
......@@ -270,11 +295,41 @@ public class BlockFinallyExtract extends AbstractVisitor {
if (removeInfo == null) {
return null;
}
if (removeInfo.getOuts().size() != 1) {
LOG.debug("Unexpected finally block outs count: {}", removeInfo.getOuts());
return null;
Set<BlocksPair> outs = removeInfo.getOuts();
if (outs.size() == 1) {
return removeInfo;
}
return removeInfo;
// check if several 'return' blocks maps to one out
if (mergeReturns(mth, outs)) {
return removeInfo;
}
LOG.debug("Unexpected finally block outs count: {}", outs);
return null;
}
private static boolean mergeReturns(MethodNode mth, Set<BlocksPair> outs) {
Set<BlockNode> rightOuts = new HashSet<BlockNode>();
boolean allReturns = true;
for (BlocksPair outPair : outs) {
BlockNode first = outPair.getFirst();
if (!first.isReturnBlock()) {
allReturns = false;
}
rightOuts.add(outPair.getSecond());
}
if (!allReturns || rightOuts.size() != 1) {
return false;
}
Iterator<BlocksPair> it = outs.iterator();
while (it.hasNext()) {
BlocksPair out = it.next();
BlockNode returnBlock = out.getFirst();
if (!returnBlock.contains(AFlag.ORIG_RETURN)) {
markForRemove(mth, returnBlock);
it.remove();
}
}
return true;
}
private static BlocksRemoveInfo checkFromFirstBlock(BlockNode remBlock, BlockNode startBlock, BitSet bs) {
......@@ -453,7 +508,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
// change start block in removeInfo
removeInfo.getProcessed().remove(removeInfo.getStart());
BlocksPair newStart = new BlocksPair(remBlock, startBlock);
removeInfo.setStart(newStart);
// removeInfo.setStart(newStart);
removeInfo.getProcessed().add(newStart);
}
// split end block
......@@ -529,6 +584,11 @@ public class BlockFinallyExtract extends AbstractVisitor {
return true;
}
/**
* Split one block into connected 2 blocks with same connections.
*
* @return new successor block
*/
private static BlockNode splitBlock(MethodNode mth, BlockNode block, int splitIndex) {
BlockNode newBlock = BlockSplitter.startNewBlock(mth, -1);
......@@ -654,6 +714,53 @@ public class BlockFinallyExtract extends AbstractVisitor {
markForRemove(mth, mb);
edgeAttr.getBlocks().remove(mb);
}
mergeSyntheticPredecessors(mth, origReturnBlock);
}
private static void mergeSyntheticPredecessors(MethodNode mth, BlockNode block) {
List<BlockNode> preds = new ArrayList<BlockNode>(block.getPredecessors());
Iterator<BlockNode> it = preds.iterator();
while (it.hasNext()) {
BlockNode predBlock = it.next();
if (!predBlock.isSynthetic()) {
it.remove();
}
}
if (preds.size() < 2) {
return;
}
BlockNode commonBlock = null;
for (BlockNode predBlock : preds) {
List<BlockNode> successors = predBlock.getSuccessors();
if (successors.size() != 2) {
return;
}
BlockNode cmnBlk = BlockUtils.selectOtherSafe(block, successors);
if (commonBlock == null) {
commonBlock = cmnBlk;
} else if (cmnBlk != commonBlock) {
return;
}
if (!predBlock.contains(AType.IGNORE_EDGE)) {
return;
}
}
if (commonBlock == null) {
return;
}
// merge confirmed
BlockNode mergeBlock = null;
for (BlockNode predBlock : preds) {
if (mergeBlock == null) {
mergeBlock = predBlock;
continue;
}
for (BlockNode remPred : predBlock.getPredecessors()) {
connect(remPred, mergeBlock);
}
markForRemove(mth, predBlock);
}
}
private static BlockNode getFinallyOutBlock(List<BlockNode> exitBlocks) {
......
......@@ -24,7 +24,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static jadx.core.dex.visitors.regions.RegionMaker.isEqualPaths;
import static jadx.core.dex.visitors.regions.RegionMaker.isReturnBlocks;
import static jadx.core.dex.visitors.regions.RegionMaker.isEqualReturnBlocks;
import static jadx.core.utils.BlockUtils.getNextBlock;
import static jadx.core.utils.BlockUtils.isPathExists;
......@@ -277,7 +277,7 @@ public class IfMakerHelper {
}
private static boolean isSameBlocks(BlockNode first, BlockNode second) {
return first == second || isReturnBlocks(first, second);
return first == second || isEqualReturnBlocks(first, second);
}
static void confirmMerge(IfInfo info) {
......
......@@ -256,17 +256,17 @@ public class ProcessVariables extends AbstractVisitor {
continue;
}
IRegion parent = region;
boolean declare = false;
boolean declared = false;
while (parent != null) {
if (canDeclareInRegion(u, region, regionsOrder)) {
declareVar(region, u.getArg());
declare = true;
declared = true;
break;
}
region = parent;
parent = region.getParent();
}
if (!declare) {
if (!declared) {
declareVar(mth.getRegion(), u.getArg());
}
}
......
......@@ -12,6 +12,8 @@ import jadx.core.dex.instructions.SwitchNode;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.Edge;
import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
......@@ -899,7 +901,7 @@ public class RegionMaker {
}
}
public void processTryCatchBlocks(MethodNode mth) {
public IRegion processTryCatchBlocks(MethodNode mth) {
Set<TryCatchBlock> tcs = new HashSet<TryCatchBlock>();
for (ExceptionHandler handler : mth.getExceptionHandlers()) {
tcs.add(handler.getTryBlock());
......@@ -935,6 +937,40 @@ public class RegionMaker {
processExcHandler(handler, exits);
}
}
return processHandlersOutBlocks(mth, tcs);
}
/**
* Search handlers successor blocks not included in any region.
*/
protected IRegion processHandlersOutBlocks(MethodNode mth, Set<TryCatchBlock> tcs) {
Set<IBlock> allRegionBlocks = new HashSet<IBlock>();
RegionUtils.getAllRegionBlocks(mth.getRegion(), allRegionBlocks);
Set<IBlock> succBlocks = new HashSet<IBlock>();
for (TryCatchBlock tc : tcs) {
for (ExceptionHandler handler : tc.getHandlers()) {
IContainer region = handler.getHandlerRegion();
if (region != null) {
IBlock lastBlock = RegionUtils.getLastBlock(region);
if (lastBlock instanceof BlockNode) {
succBlocks.addAll(((BlockNode) lastBlock).getSuccessors());
}
RegionUtils.getAllRegionBlocks(region, allRegionBlocks);
}
}
}
succBlocks.removeAll(allRegionBlocks);
if (succBlocks.isEmpty()) {
return null;
}
Region excOutRegion = new Region(mth.getRegion());
for (IBlock block : succBlocks) {
if (block instanceof BlockNode) {
excOutRegion.add(makeRegion((BlockNode) block, new RegionStack(mth)));
}
}
return excOutRegion;
}
private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
......@@ -980,7 +1016,7 @@ public class RegionMaker {
if (b1 == null || b2 == null) {
return false;
}
return isReturnBlocks(b1, b2) || isSyntheticPath(b1, b2);
return isEqualReturnBlocks(b1, b2) || isSyntheticPath(b1, b2);
}
private static boolean isSyntheticPath(BlockNode b1, BlockNode b2) {
......@@ -989,7 +1025,7 @@ public class RegionMaker {
return (n1 != b1 || n2 != b2) && isEqualPaths(n1, n2);
}
public static boolean isReturnBlocks(BlockNode b1, BlockNode b2) {
public static boolean isEqualReturnBlocks(BlockNode b1, BlockNode b2) {
if (!b1.isReturnBlock() || !b2.isReturnBlock()) {
return false;
}
......
......@@ -49,7 +49,10 @@ public class RegionMakerVisitor extends AbstractVisitor {
mth.setRegion(rm.makeRegion(mth.getEnterBlock(), state));
if (!mth.isNoExceptionHandlers()) {
rm.processTryCatchBlocks(mth);
IRegion expOutBlock = rm.processTryCatchBlocks(mth);
if (expOutBlock != null) {
mth.getRegion().add(expOutBlock);
}
}
postProcessRegions(mth);
......
......@@ -2,12 +2,17 @@ package jadx.core.dex.visitors.ssa;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.PhiInsn;
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.dex.visitors.AbstractVisitor;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.Iterator;
import java.util.List;
......@@ -23,6 +28,7 @@ public class EliminatePhiNodes extends AbstractVisitor {
if (mth.isNoCode()) {
return;
}
replaceMergeInstructions(mth);
removePhiInstructions(mth);
}
......@@ -50,4 +56,75 @@ public class EliminatePhiNodes extends AbstractVisitor {
}
LOG.warn("Phi node not removed: {}, mth: {}", phiInsn, mth);
}
private void replaceMergeInstructions(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
List<InsnNode> insns = block.getInstructions();
if (insns.isEmpty()) {
continue;
}
InsnNode insn = insns.get(0);
if (insn.getType() == InsnType.MERGE) {
replaceMerge(mth, block, insn);
}
}
}
/**
* Replace 'MERGE' with 'PHI' insn.
*/
private void replaceMerge(MethodNode mth, BlockNode block, InsnNode insn) {
if (insn.getArgsCount() != 2) {
throw new JadxRuntimeException("Unexpected count of arguments in merge insn: " + insn);
}
RegisterArg oldArg = (RegisterArg) insn.getArg(1);
RegisterArg newArg = (RegisterArg) insn.getArg(0);
int newRegNum = newArg.getRegNum();
if (oldArg.getRegNum() == newRegNum) {
throw new JadxRuntimeException("Unexpected register number in merge insn: " + insn);
}
SSAVar oldSVar = oldArg.getSVar();
RegisterArg assignArg = oldSVar.getAssign();
InsnNode assignParentInsn = assignArg.getParentInsn();
BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignParentInsn);
if (assignBlock == null) {
throw new JadxRuntimeException("Unknown assign block for " + assignParentInsn);
}
BlockNode assignPred = null;
for (BlockNode pred : block.getPredecessors()) {
if (BlockUtils.isPathExists(assignBlock, pred)) {
assignPred = pred;
break;
}
}
if (assignPred == null) {
throw new JadxRuntimeException("Assign predecessor not found for " + assignBlock + " from " + block);
}
// all checks passed
RegisterArg newAssignArg = oldArg.duplicate(newRegNum, null);
SSAVar newSVar = mth.makeNewSVar(newRegNum, mth.getNextSVarVersion(newRegNum), newAssignArg);
newSVar.setName(oldSVar.getName());
newSVar.setType(assignArg.getType());
if (assignParentInsn != null) {
assignParentInsn.setResult(newAssignArg);
}
for (RegisterArg useArg : oldSVar.getUseList()) {
RegisterArg newUseArg = useArg.duplicate(newRegNum, newSVar);
InsnNode parentInsn = useArg.getParentInsn();
if (parentInsn != null) {
newSVar.use(newUseArg);
parentInsn.replaceArg(useArg, newUseArg);
}
}
block.getInstructions().remove(0);
PhiInsn phiInsn = SSATransform.addPhi(mth, block, newRegNum);
phiInsn.setResult(insn.getResult());
phiInsn.bindArg(newAssignArg.duplicate(), assignPred);
phiInsn.bindArg(newArg.duplicate(),
BlockUtils.selectOtherSafe(assignPred, block.getPredecessors()));
}
}
......@@ -10,7 +10,12 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.BitSet;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LiveVarAnalysis {
private static final Logger LOG = LoggerFactory.getLogger(LiveVarAnalysis.class);
private final MethodNode mth;
private BitSet[] uses;
......@@ -37,9 +42,17 @@ public class LiveVarAnalysis {
}
public boolean isLive(int blockId, int regNum) {
if (blockId >= liveIn.length) {
LOG.warn("LiveVarAnalysis: out of bounds block: {}, max: {}", blockId, liveIn.length);
return false;
}
return liveIn[blockId].get(regNum);
}
public boolean isLive(BlockNode block, int regNum) {
return isLive(block.getId(), regNum);
}
private void fillBasicBlockInfo() {
for (BlockNode block : mth.getBasicBlocks()) {
int blockId = block.getId();
......
......@@ -93,7 +93,7 @@ public class SSATransform extends AbstractVisitor {
}
}
private static void addPhi(MethodNode mth, BlockNode block, int regNum) {
public static PhiInsn addPhi(MethodNode mth, BlockNode block, int regNum) {
PhiListAttr phiList = block.get(AType.PHI_LIST);
if (phiList == null) {
phiList = new PhiListAttr();
......@@ -112,6 +112,7 @@ public class SSATransform extends AbstractVisitor {
phiList.getList().add(phiInsn);
phiInsn.setOffset(block.getStartOffset());
block.getInstructions().add(0, phiInsn);
return phiInsn;
}
private static void renameVariables(MethodNode mth) {
......@@ -124,13 +125,18 @@ public class SSATransform extends AbstractVisitor {
// init method arguments
for (RegisterArg arg : mth.getArguments(true)) {
int regNum = arg.getRegNum();
vars[regNum] = mth.makeNewSVar(regNum, versions, arg);
vars[regNum] = newSSAVar(mth, versions, arg, regNum);
}
BlockNode enterBlock = mth.getEnterBlock();
initPhiInEnterBlock(vars, enterBlock);
renameVar(mth, vars, versions, enterBlock);
}
private static SSAVar newSSAVar(MethodNode mth, int[] versions, RegisterArg arg, int regNum) {
int version = versions[regNum]++;
return mth.makeNewSVar(regNum, version, arg);
}
private static void initPhiInEnterBlock(SSAVar[] vars, BlockNode enterBlock) {
PhiListAttr phiList = enterBlock.get(AType.PHI_LIST);
if (phiList != null) {
......@@ -168,7 +174,7 @@ public class SSATransform extends AbstractVisitor {
RegisterArg result = insn.getResult();
if (result != null) {
int regNum = result.getRegNum();
vars[regNum] = mth.makeNewSVar(regNum, vers, result);
vars[regNum] = newSSAVar(mth, vers, result, regNum);
}
}
for (BlockNode s : block.getSuccessors()) {
......
......@@ -3,7 +3,6 @@ package jadx.core.dex.visitors.typeinference;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.LiteralArg;
......@@ -101,11 +100,11 @@ public class PostTypeInference {
return true;
}
case PHI: {
PhiInsn phi = (PhiInsn) insn;
ArgType type = phi.getResult().getType();
case PHI:
case MERGE: {
ArgType type = insn.getResult().getType();
if (!type.isTypeKnown()) {
for (InsnArg arg : phi.getArguments()) {
for (InsnArg arg : insn.getArguments()) {
if (arg.getType().isTypeKnown()) {
type = arg.getType();
break;
......@@ -113,11 +112,11 @@ public class PostTypeInference {
}
}
boolean changed = false;
if (updateType(phi.getResult(), type)) {
if (updateType(insn.getResult(), type)) {
changed = true;
}
for (int i = 0; i < phi.getArgsCount(); i++) {
RegisterArg arg = phi.getArg(i);
for (int i = 0; i < insn.getArgsCount(); i++) {
RegisterArg arg = (RegisterArg) insn.getArg(i);
if (updateType(arg, type)) {
changed = true;
}
......
......@@ -36,7 +36,11 @@ public class DebugUtils {
private static final Logger LOG = LoggerFactory.getLogger(DebugUtils.class);
public static void dump(MethodNode mth) {
File out = new File("test-graph-tmp");
dump(mth, "");
}
public static void dump(MethodNode mth, String desc) {
File out = new File("test-graph" + desc + "-tmp");
DotGraphVisitor.dump(out).visit(mth);
DotGraphVisitor.dumpRaw(out).visit(mth);
DotGraphVisitor.dumpRegions(out).visit(mth);
......
......@@ -72,6 +72,22 @@ public class RegionUtils {
}
}
public static IBlock getLastBlock(IContainer container) {
if (container instanceof IBlock) {
return (IBlock) container;
} else if (container instanceof IBranchRegion) {
return null;
} else if (container instanceof IRegion) {
List<IContainer> blocks = ((IRegion) container).getSubBlocks();
if (blocks.isEmpty()) {
return null;
}
return getLastBlock(blocks.get(blocks.size() - 1));
} else {
throw new JadxRuntimeException(unknownContainerType(container));
}
}
/**
* Return true if last block in region has no successors
*/
......
package jadx.tests.integration.trycatch;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsLines;
import static org.junit.Assert.assertThat;
public class TestTryCatchFinally6 extends IntegrationTest {
public static class TestCls {
public static void test() throws IOException {
InputStream is = null;
try {
is = new FileInputStream("1.txt");
} finally {
if (is != null) {
is.close();
}
}
}
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsLines(2,
"InputStream is = null;",
"try {",
indent(1) + "is = new FileInputStream(\"1.txt\");",
"} finally {",
indent(1) + "if (is != null) {",
indent(2) + "is.close();",
indent(1) + "}",
"}"
));
}
}
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