core: fix some 'try/catch/finally' cases
This commit is contained in:
@@ -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;
|
||||
|
||||
+127
-20
@@ -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
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
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) + "}",
|
||||
"}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user