@@ -4,7 +4,7 @@ import jadx.api.IJadxArgs;
|
||||
import jadx.core.codegen.CodeGen;
|
||||
import jadx.core.dex.visitors.ClassModifier;
|
||||
import jadx.core.dex.visitors.CodeShrinker;
|
||||
import jadx.core.dex.visitors.ConstInlinerVisitor;
|
||||
import jadx.core.dex.visitors.ConstInlineVisitor;
|
||||
import jadx.core.dex.visitors.DebugInfoVisitor;
|
||||
import jadx.core.dex.visitors.DotGraphVisitor;
|
||||
import jadx.core.dex.visitors.EnumVisitor;
|
||||
@@ -73,7 +73,7 @@ public class Jadx {
|
||||
passes.add(DotGraphVisitor.dumpRaw(outDir));
|
||||
}
|
||||
|
||||
passes.add(new ConstInlinerVisitor());
|
||||
passes.add(new ConstInlineVisitor());
|
||||
passes.add(new FinishTypeInference());
|
||||
passes.add(new EliminatePhiNodes());
|
||||
|
||||
|
||||
@@ -442,11 +442,6 @@ public class InsnGen {
|
||||
addArg(code, insn.getArg(0));
|
||||
break;
|
||||
|
||||
case PHI:
|
||||
assert isFallback();
|
||||
code.add("PHI(").add(String.valueOf(insn.getArgsCount())).add(")");
|
||||
break;
|
||||
|
||||
/* fallback mode instructions */
|
||||
case IF:
|
||||
assert isFallback() : "if insn in not fallback mode";
|
||||
|
||||
@@ -4,35 +4,93 @@ import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
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;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class PhiInsn extends InsnNode {
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class PhiInsn extends InsnNode {
|
||||
|
||||
private final Map<RegisterArg, BlockNode> blockBinds;
|
||||
|
||||
public PhiInsn(int regNum, int predecessors) {
|
||||
super(InsnType.PHI, predecessors);
|
||||
this.blockBinds = new IdentityHashMap<RegisterArg, BlockNode>(predecessors);
|
||||
setResult(InsnArg.reg(regNum, ArgType.UNKNOWN));
|
||||
for (int i = 0; i < predecessors; i++) {
|
||||
addReg(regNum, ArgType.UNKNOWN);
|
||||
}
|
||||
add(AFlag.DONT_INLINE);
|
||||
}
|
||||
|
||||
public RegisterArg bindArg(BlockNode pred) {
|
||||
RegisterArg arg = InsnArg.reg(getResult().getRegNum(), getResult().getType());
|
||||
bindArg(arg, pred);
|
||||
return arg;
|
||||
}
|
||||
|
||||
public void bindArg(RegisterArg arg, BlockNode pred) {
|
||||
if (blockBinds.containsValue(pred)) {
|
||||
throw new JadxRuntimeException("Duplicate predecessors in PHI insn: " + pred + ", " + this);
|
||||
}
|
||||
addArg(arg);
|
||||
blockBinds.put(arg, pred);
|
||||
}
|
||||
|
||||
public BlockNode getBlockByArg(RegisterArg arg) {
|
||||
return blockBinds.get(arg);
|
||||
}
|
||||
|
||||
public Map<RegisterArg, BlockNode> getBlockBinds() {
|
||||
return blockBinds;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public RegisterArg getArg(int n) {
|
||||
return (RegisterArg) super.getArg(n);
|
||||
}
|
||||
|
||||
public boolean removeArg(RegisterArg arg) {
|
||||
boolean isRemoved = super.removeArg(arg);
|
||||
if (isRemoved) {
|
||||
arg.getSVar().setUsedInPhi(null);
|
||||
@Override
|
||||
public boolean removeArg(InsnArg arg) {
|
||||
if (!(arg instanceof RegisterArg)) {
|
||||
return false;
|
||||
}
|
||||
return isRemoved;
|
||||
RegisterArg reg = (RegisterArg) arg;
|
||||
if (super.removeArg(reg)) {
|
||||
blockBinds.remove(reg);
|
||||
InstructionRemover.fixUsedInPhiFlag(reg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceArg(InsnArg from, InsnArg to) {
|
||||
if (!(from instanceof RegisterArg) || !(to instanceof RegisterArg)) {
|
||||
return false;
|
||||
}
|
||||
BlockNode pred = getBlockByArg((RegisterArg) from);
|
||||
if (pred == null) {
|
||||
throw new JadxRuntimeException("Unknown predecessor block by arg " + from + " in PHI: " + this);
|
||||
}
|
||||
if (removeArg(from)) {
|
||||
bindArg((RegisterArg) to, pred);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArg(int n, InsnArg arg) {
|
||||
throw new JadxRuntimeException("Unsupported operation for PHI node");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments());
|
||||
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments())
|
||||
+ " binds: " + blockBinds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.parser.FieldValueAttr;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -44,7 +45,7 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
return sVar;
|
||||
}
|
||||
|
||||
void setSVar(SSAVar sVar) {
|
||||
void setSVar(@NotNull SSAVar sVar) {
|
||||
this.sVar = sVar;
|
||||
}
|
||||
|
||||
@@ -162,7 +163,7 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (regNum * 31 + type.hashCode()) * 31 + (sVar != null ? sVar.hashCode() : 0);
|
||||
return regNum * 31 + type.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -111,6 +111,10 @@ public class InsnNode extends LineAttrNode {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (arg == arguments.get(i)) {
|
||||
arguments.remove(i);
|
||||
if (arg instanceof RegisterArg) {
|
||||
RegisterArg reg = (RegisterArg) arg;
|
||||
reg.getSVar().removeUse(reg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,26 @@ public class TryCatchBlock {
|
||||
private void unbindHandler(ExceptionHandler handler) {
|
||||
for (BlockNode block : handler.getBlocks()) {
|
||||
block.add(AFlag.SKIP);
|
||||
ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);
|
||||
if (excHandlerAttr != null) {
|
||||
if (excHandlerAttr.getHandler().equals(handler)) {
|
||||
block.remove(AType.EXC_HANDLER);
|
||||
}
|
||||
}
|
||||
SplitterBlockAttr splitter = handler.getHandlerBlock().get(AType.SPLITTER_BLOCK);
|
||||
if (splitter != null) {
|
||||
splitter.getBlock().remove(AType.SPLITTER_BLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeWholeBlock(MethodNode mth) {
|
||||
// self destruction
|
||||
for (Iterator<ExceptionHandler> it = handlers.iterator(); it.hasNext(); ) {
|
||||
ExceptionHandler h = it.next();
|
||||
unbindHandler(h);
|
||||
it.remove();
|
||||
}
|
||||
for (InsnNode insn : insns) {
|
||||
insn.removeAttr(attr);
|
||||
}
|
||||
@@ -83,9 +98,22 @@ public class TryCatchBlock {
|
||||
insn.addAttr(attr);
|
||||
}
|
||||
|
||||
public void removeInsn(InsnNode insn) {
|
||||
public void removeInsn(MethodNode mth, InsnNode insn) {
|
||||
insns.remove(insn);
|
||||
insn.remove(AType.CATCH_BLOCK);
|
||||
if (insns.isEmpty()) {
|
||||
removeWholeBlock(mth);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeBlock(MethodNode mth, BlockNode block) {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
insns.remove(insn);
|
||||
insn.remove(AType.CATCH_BLOCK);
|
||||
}
|
||||
if (insns.isEmpty()) {
|
||||
removeWholeBlock(mth);
|
||||
}
|
||||
}
|
||||
|
||||
public Iterable<InsnNode> getInsns() {
|
||||
|
||||
+3
-5
@@ -23,7 +23,7 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
public class ConstInlineVisitor extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(MethodNode mth) throws JadxException {
|
||||
@@ -38,14 +38,12 @@ public class ConstInlinerVisitor extends AbstractVisitor {
|
||||
toRemove.add(insn);
|
||||
}
|
||||
}
|
||||
if (!toRemove.isEmpty()) {
|
||||
InstructionRemover.removeAll(mth, block, toRemove);
|
||||
}
|
||||
InstructionRemover.removeAll(mth, block, toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkInsn(MethodNode mth, InsnNode insn) {
|
||||
if (insn.getType() != InsnType.CONST) {
|
||||
if (insn.getType() != InsnType.CONST || insn.contains(AFlag.DONT_INLINE)) {
|
||||
return false;
|
||||
}
|
||||
InsnArg arg = insn.getArg(0);
|
||||
@@ -33,7 +33,7 @@ public class FallbackModeVisitor extends AbstractVisitor {
|
||||
case CONST_CLASS:
|
||||
case CMP_L:
|
||||
case CMP_G:
|
||||
catchAttr.getTryBlock().removeInsn(insn);
|
||||
catchAttr.getTryBlock().removeInsn(mth, insn);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
+225
-76
@@ -9,7 +9,7 @@ import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.SplitterBlockAttr;
|
||||
import jadx.core.dex.trycatch.TryCatchBlock;
|
||||
@@ -18,7 +18,6 @@ import jadx.core.dex.visitors.blocksmaker.helpers.BlocksPair;
|
||||
import jadx.core.dex.visitors.blocksmaker.helpers.BlocksRemoveInfo;
|
||||
import jadx.core.dex.visitors.ssa.LiveVarAnalysis;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -35,7 +34,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.connect;
|
||||
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.insertBlockBetween;
|
||||
import static jadx.core.dex.visitors.blocksmaker.BlockSplitter.removeConnection;
|
||||
|
||||
public class BlockFinallyExtract extends AbstractVisitor {
|
||||
@@ -48,10 +46,8 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
}
|
||||
|
||||
boolean reloadBlocks = false;
|
||||
List<BlockNode> basicBlocks = mth.getBasicBlocks();
|
||||
for (int i = 0; i < basicBlocks.size(); i++) {
|
||||
BlockNode block = basicBlocks.get(i);
|
||||
if (processExceptionHandler(mth, block)) {
|
||||
for (ExceptionHandler excHandler : mth.getExceptionHandlers()) {
|
||||
if (processExceptionHandler(mth, excHandler)) {
|
||||
reloadBlocks = true;
|
||||
}
|
||||
}
|
||||
@@ -61,13 +57,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean processExceptionHandler(MethodNode mth, BlockNode block) {
|
||||
ExcHandlerAttr handlerAttr = block.get(AType.EXC_HANDLER);
|
||||
if (handlerAttr == null) {
|
||||
return false;
|
||||
}
|
||||
ExceptionHandler excHandler = handlerAttr.getHandler();
|
||||
|
||||
private static boolean processExceptionHandler(MethodNode mth, ExceptionHandler excHandler) {
|
||||
// check if handler has exit edge to block not from this handler
|
||||
boolean noExitNode = true;
|
||||
boolean reThrowRemoved = false;
|
||||
@@ -82,16 +72,16 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
&& size != 0
|
||||
&& insns.get(size - 1).getType() == InsnType.THROW) {
|
||||
reThrowRemoved = true;
|
||||
InstructionRemover.remove(mth, excBlock, size - 1);
|
||||
insns.remove(size - 1);
|
||||
}
|
||||
}
|
||||
if (reThrowRemoved && noExitNode
|
||||
&& extractFinally(mth, block, excHandler)) {
|
||||
&& extractFinally(mth, excHandler)) {
|
||||
return true;
|
||||
}
|
||||
int totalSize = countInstructions(excHandler);
|
||||
if (totalSize == 0 && reThrowRemoved && noExitNode) {
|
||||
handlerAttr.getTryBlock().removeHandler(mth, excHandler);
|
||||
excHandler.getTryBlock().removeHandler(mth, excHandler);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -99,7 +89,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
/**
|
||||
* Search and remove common code from 'catch' and 'handlers'.
|
||||
*/
|
||||
private static boolean extractFinally(MethodNode mth, BlockNode handlerBlock, ExceptionHandler handler) {
|
||||
private static boolean extractFinally(MethodNode mth, ExceptionHandler handler) {
|
||||
int count = handler.getBlocks().size();
|
||||
BitSet bs = new BitSet(count);
|
||||
List<BlockNode> blocks = new ArrayList<BlockNode>(count);
|
||||
@@ -171,21 +161,105 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 'finally' extract confirmed
|
||||
/* 'finally' extract confirmed, run remove steps */
|
||||
|
||||
LiveVarAnalysis laBefore = null;
|
||||
boolean runReMap = isReMapNeeded(removes);
|
||||
if (runReMap) {
|
||||
laBefore = new LiveVarAnalysis(mth);
|
||||
laBefore.runAnalysis();
|
||||
}
|
||||
|
||||
for (BlocksRemoveInfo removeInfo : removes) {
|
||||
if (!applyRemove(mth, removeInfo)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
handler.setFinally(true);
|
||||
|
||||
LiveVarAnalysis laAfter = null;
|
||||
|
||||
// remove 'move-exception' instruction
|
||||
if (BlockUtils.checkLastInsnType(handlerBlock, InsnType.MOVE_EXCEPTION)) {
|
||||
InstructionRemover.remove(mth, handlerBlock, handlerBlock.getInstructions().size() - 1);
|
||||
handlerBlock.add(AFlag.SKIP);
|
||||
BlockNode handlerBlock = handler.getHandlerBlock();
|
||||
InsnNode me = BlockUtils.getLastInsn(handlerBlock);
|
||||
if (me != null && me.getType() == InsnType.MOVE_EXCEPTION) {
|
||||
boolean replaced = false;
|
||||
List<InsnNode> insnsList = handlerBlock.getInstructions();
|
||||
if (!handlerBlock.getCleanSuccessors().isEmpty()) {
|
||||
laAfter = new LiveVarAnalysis(mth);
|
||||
laAfter.runAnalysis();
|
||||
|
||||
RegisterArg resArg = me.getResult();
|
||||
BlockNode succ = handlerBlock.getCleanSuccessors().get(0);
|
||||
if (laAfter.isLive(succ.getId(), resArg.getRegNum())) {
|
||||
// kill variable
|
||||
InsnNode kill = new InsnNode(InsnType.NOP, 0);
|
||||
kill.setResult(resArg);
|
||||
kill.add(AFlag.REMOVE);
|
||||
insnsList.set(insnsList.size() - 1, kill);
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
insnsList.remove(insnsList.size() - 1);
|
||||
handlerBlock.add(AFlag.SKIP);
|
||||
}
|
||||
}
|
||||
|
||||
// generate 'move' instruction for mapped register pairs
|
||||
if (runReMap) {
|
||||
if (laAfter == null) {
|
||||
laAfter = new LiveVarAnalysis(mth);
|
||||
laAfter.runAnalysis();
|
||||
}
|
||||
performVariablesReMap(mth, removes, laBefore, laAfter);
|
||||
}
|
||||
|
||||
handler.setFinally(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void performVariablesReMap(MethodNode mth, List<BlocksRemoveInfo> removes,
|
||||
LiveVarAnalysis laBefore, LiveVarAnalysis laAfter) {
|
||||
BitSet processed = new BitSet(mth.getRegsCount());
|
||||
for (BlocksRemoveInfo removeInfo : removes) {
|
||||
processed.clear();
|
||||
BlockNode insertBlock = removeInfo.getStart().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)) {
|
||||
// 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)) {
|
||||
// kill variable
|
||||
InsnNode kill = new InsnNode(InsnType.NOP, 0);
|
||||
kill.setResult(from);
|
||||
kill.add(AFlag.REMOVE);
|
||||
insertBlock.getInstructions().add(0, kill);
|
||||
}
|
||||
processed.set(regNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isReMapNeeded(List<BlocksRemoveInfo> removes) {
|
||||
for (BlocksRemoveInfo removeInfo : removes) {
|
||||
if (!removeInfo.getRegMap().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static BlocksRemoveInfo removeInsns(MethodNode mth, BlockNode remBlock, List<BlockNode> blocks, BitSet bs) {
|
||||
if (blocks.isEmpty()) {
|
||||
return null;
|
||||
@@ -223,14 +297,36 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
return null;
|
||||
}
|
||||
// first - fast check
|
||||
int delta = remInsns.size() - startInsns.size();
|
||||
if (!checkInsns(remInsns, startInsns, delta, null)) {
|
||||
return null;
|
||||
int startPos = remInsns.size() - startInsns.size();
|
||||
int endPos = 0;
|
||||
if (!checkInsns(remInsns, startInsns, startPos, null)) {
|
||||
if (checkInsns(remInsns, startInsns, 0, null)) {
|
||||
startPos = 0;
|
||||
endPos = startInsns.size();
|
||||
} else {
|
||||
boolean found = false;
|
||||
for (int i = 1; i < startPos; i++) {
|
||||
if (checkInsns(remInsns, startInsns, i, null)) {
|
||||
startPos = i;
|
||||
endPos = startInsns.size() + i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
BlocksPair startPair = new BlocksPair(remBlock, startBlock);
|
||||
BlocksRemoveInfo removeInfo = new BlocksRemoveInfo(startPair);
|
||||
removeInfo.setStartSplitIndex(startPos);
|
||||
removeInfo.setEndSplitIndex(endPos);
|
||||
if (endPos != 0) {
|
||||
removeInfo.setEnd(startPair);
|
||||
}
|
||||
BlocksRemoveInfo removeInfo = new BlocksRemoveInfo(new BlocksPair(remBlock, startBlock));
|
||||
removeInfo.setStartSplitIndex(delta);
|
||||
// second - run checks again for collect registers mapping
|
||||
if (!checkInsns(remInsns, startInsns, delta, removeInfo)) {
|
||||
if (!checkInsns(remInsns, startInsns, startPos, removeInfo)) {
|
||||
return null;
|
||||
}
|
||||
return removeInfo;
|
||||
@@ -255,18 +351,23 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
&& !sameBlocks(remBlock, startBlock, removeInfo)) {
|
||||
return false;
|
||||
}
|
||||
removeInfo.getProcessed().add(new BlocksPair(remBlock, startBlock));
|
||||
BlocksPair currentPair = new BlocksPair(remBlock, startBlock);
|
||||
removeInfo.getProcessed().add(currentPair);
|
||||
|
||||
List<BlockNode> baseCS = startBlock.getCleanSuccessors();
|
||||
List<BlockNode> remCS = remBlock.getCleanSuccessors();
|
||||
if (baseCS.size() != remCS.size()) {
|
||||
removeInfo.getOuts().add(new BlocksPair(remBlock, startBlock));
|
||||
removeInfo.getOuts().add(currentPair);
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < baseCS.size(); i++) {
|
||||
BlockNode sBlock = baseCS.get(i);
|
||||
BlockNode rBlock = remCS.get(i);
|
||||
if (bs.get(sBlock.getId())) {
|
||||
if (removeInfo.getEndSplitIndex() != 0) {
|
||||
// end block is not correct
|
||||
return false;
|
||||
}
|
||||
if (!checkBlocksTree(rBlock, sBlock, removeInfo, bs)) {
|
||||
return false;
|
||||
}
|
||||
@@ -277,18 +378,22 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean sameBlocks(BlockNode remBlock, BlockNode startBlock, BlocksRemoveInfo removeInfo) {
|
||||
private static boolean sameBlocks(BlockNode remBlock, BlockNode finallyBlock, BlocksRemoveInfo removeInfo) {
|
||||
List<InsnNode> first = remBlock.getInstructions();
|
||||
List<InsnNode> second = startBlock.getInstructions();
|
||||
if (first.size() != second.size()) {
|
||||
List<InsnNode> second = finallyBlock.getInstructions();
|
||||
if (first.size() < second.size()) {
|
||||
return false;
|
||||
}
|
||||
int size = first.size();
|
||||
int size = second.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!sameInsns(first.get(i), second.get(i), removeInfo)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (first.size() > second.size()) {
|
||||
removeInfo.setEndSplitIndex(second.size());
|
||||
removeInfo.setEnd(new BlocksPair(remBlock, finallyBlock));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -332,27 +437,43 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
LOG.warn("Finally extract failed: remBlock pred: {}, {}, method: {}", remBlock, remBlock.getPredecessors(), mth);
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockNode remBlockPred = remBlock.getPredecessors().get(0);
|
||||
int splitIndex = removeInfo.getStartSplitIndex();
|
||||
if (splitIndex > 0) {
|
||||
// split start block (remBlock)
|
||||
BlockNode newBlock = insertBlockBetween(mth, remBlockPred, remBlock);
|
||||
for (int i = 0; i < splitIndex; i++) {
|
||||
InsnNode insnNode = remBlock.getInstructions().get(i);
|
||||
insnNode.add(AFlag.SKIP);
|
||||
newBlock.getInstructions().add(insnNode);
|
||||
}
|
||||
Iterator<InsnNode> it = remBlock.getInstructions().iterator();
|
||||
while (it.hasNext()) {
|
||||
InsnNode insnNode = it.next();
|
||||
if (insnNode.contains(AFlag.SKIP)) {
|
||||
it.remove();
|
||||
removeInfo.setStartPredecessor(remBlockPred);
|
||||
|
||||
int startSplitIndex = removeInfo.getStartSplitIndex();
|
||||
int endSplitIndex = removeInfo.getEndSplitIndex();
|
||||
if (removeInfo.getStart().equals(removeInfo.getEnd())) {
|
||||
removeInfo.setEndSplitIndex(endSplitIndex - startSplitIndex);
|
||||
}
|
||||
// split start block (remBlock)
|
||||
if (startSplitIndex > 0) {
|
||||
remBlock = splitBlock(mth, remBlock, startSplitIndex);
|
||||
// change start block in removeInfo
|
||||
removeInfo.getProcessed().remove(removeInfo.getStart());
|
||||
BlocksPair newStart = new BlocksPair(remBlock, startBlock);
|
||||
removeInfo.setStart(newStart);
|
||||
removeInfo.getProcessed().add(newStart);
|
||||
}
|
||||
// split end block
|
||||
if (endSplitIndex > 0) {
|
||||
BlocksPair end = removeInfo.getEnd();
|
||||
BlockNode newOut = splitBlock(mth, end.getFirst(), endSplitIndex);
|
||||
for (BlockNode s : newOut.getSuccessors()) {
|
||||
BlocksPair replaceOut = null;
|
||||
Iterator<BlocksPair> it = removeInfo.getOuts().iterator();
|
||||
while (it.hasNext()) {
|
||||
BlocksPair outPair = it.next();
|
||||
if (outPair.getFirst().equals(s)) {
|
||||
it.remove();
|
||||
replaceOut = new BlocksPair(newOut, outPair.getSecond());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (replaceOut != null) {
|
||||
removeInfo.getOuts().add(replaceOut);
|
||||
}
|
||||
}
|
||||
for (InsnNode insnNode : newBlock.getInstructions()) {
|
||||
insnNode.remove(AFlag.SKIP);
|
||||
}
|
||||
remBlockPred = newBlock;
|
||||
}
|
||||
|
||||
BlocksPair out = removeInfo.getOuts().iterator().next();
|
||||
@@ -377,8 +498,8 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
BlockNode pred = filtPreds.get(0);
|
||||
BlockNode repl = removeInfo.getBySecond(pred);
|
||||
if (repl == null) {
|
||||
throw new JadxRuntimeException("Block not found by " + pred
|
||||
+ ", in " + removeInfo + ", method: " + mth);
|
||||
LOG.error("Block not found by {}, in {}, method: {}", pred, removeInfo, mth);
|
||||
return false;
|
||||
}
|
||||
removeConnection(pred, rOut);
|
||||
addIgnoredEdge(repl, rOut);
|
||||
@@ -396,39 +517,56 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
connect(pred, rOut);
|
||||
}
|
||||
|
||||
// generate 'move' instruction for mapped register pairs
|
||||
if (!removeInfo.getRegMap().isEmpty()) {
|
||||
// TODO: very expensive operation
|
||||
LiveVarAnalysis la = new LiveVarAnalysis(mth);
|
||||
la.runAnalysis();
|
||||
for (Map.Entry<RegisterArg, RegisterArg> entry : removeInfo.getRegMap().entrySet()) {
|
||||
RegisterArg from = entry.getKey();
|
||||
if (la.isLive(remBlockPred.getId(), from.getRegNum())) {
|
||||
RegisterArg to = entry.getValue();
|
||||
InsnNode move = new InsnNode(InsnType.MOVE, 1);
|
||||
move.setResult(to);
|
||||
move.addArg(from);
|
||||
remBlockPred.getInstructions().add(move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark blocks for remove
|
||||
markForRemove(remBlock);
|
||||
markForRemove(mth, remBlock);
|
||||
for (BlocksPair pair : removeInfo.getProcessed()) {
|
||||
markForRemove(pair.getFirst());
|
||||
markForRemove(mth, pair.getFirst());
|
||||
BlockNode second = pair.getSecond();
|
||||
second.updateCleanSuccessors();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static BlockNode splitBlock(MethodNode mth, BlockNode block, int splitIndex) {
|
||||
BlockNode newBlock = BlockSplitter.startNewBlock(mth, -1);
|
||||
|
||||
newBlock.getSuccessors().addAll(block.getSuccessors());
|
||||
for (BlockNode s : new ArrayList<BlockNode>(block.getSuccessors())) {
|
||||
removeConnection(block, s);
|
||||
connect(newBlock, s);
|
||||
}
|
||||
block.getSuccessors().clear();
|
||||
connect(block, newBlock);
|
||||
block.updateCleanSuccessors();
|
||||
newBlock.updateCleanSuccessors();
|
||||
|
||||
List<InsnNode> insns = block.getInstructions();
|
||||
int size = insns.size();
|
||||
for (int i = splitIndex; i < size; i++) {
|
||||
InsnNode insnNode = insns.get(i);
|
||||
insnNode.add(AFlag.SKIP);
|
||||
newBlock.getInstructions().add(insnNode);
|
||||
}
|
||||
Iterator<InsnNode> it = insns.iterator();
|
||||
while (it.hasNext()) {
|
||||
InsnNode insnNode = it.next();
|
||||
if (insnNode.contains(AFlag.SKIP)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
for (InsnNode insnNode : newBlock.getInstructions()) {
|
||||
insnNode.remove(AFlag.SKIP);
|
||||
}
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind block for removing.
|
||||
*/
|
||||
private static void markForRemove(BlockNode block) {
|
||||
private static void markForRemove(MethodNode mth, BlockNode block) {
|
||||
for (BlockNode p : block.getPredecessors()) {
|
||||
p.getSuccessors().remove(block);
|
||||
p.updateCleanSuccessors();
|
||||
}
|
||||
for (BlockNode s : block.getSuccessors()) {
|
||||
s.getPredecessors().remove(block);
|
||||
@@ -436,6 +574,17 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
block.getPredecessors().clear();
|
||||
block.getSuccessors().clear();
|
||||
block.add(AFlag.REMOVE);
|
||||
block.remove(AFlag.SKIP);
|
||||
|
||||
CatchAttr catchAttr = block.get(AType.CATCH_BLOCK);
|
||||
if (catchAttr != null) {
|
||||
catchAttr.getTryBlock().removeBlock(mth, block);
|
||||
for (BlockNode skipBlock : mth.getBasicBlocks()) {
|
||||
if (skipBlock.contains(AFlag.SKIP)) {
|
||||
markForRemove(mth, skipBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addIgnoredEdge(BlockNode from, BlockNode toBlock) {
|
||||
@@ -500,7 +649,7 @@ public class BlockFinallyExtract extends AbstractVisitor {
|
||||
for (BlockNode remPred : mb.getPredecessors()) {
|
||||
connect(remPred, origReturnBlock);
|
||||
}
|
||||
markForRemove(mb);
|
||||
markForRemove(mth, mb);
|
||||
edgeAttr.getBlocks().remove(mb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.Edge;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -396,6 +397,10 @@ public class BlockProcessor extends AbstractVisitor {
|
||||
|| !block.getSuccessors().isEmpty()) {
|
||||
LOG.error("Block {} not deleted, method: {}", block, mth);
|
||||
} else {
|
||||
CatchAttr catchAttr = block.get(AType.CATCH_BLOCK);
|
||||
if (catchAttr != null) {
|
||||
catchAttr.getTryBlock().removeBlock(mth, block);
|
||||
}
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
+35
-1
@@ -14,9 +14,14 @@ public final class BlocksRemoveInfo {
|
||||
private final Set<BlocksPair> processed = new HashSet<BlocksPair>();
|
||||
private final Set<BlocksPair> outs = new HashSet<BlocksPair>();
|
||||
private final Map<RegisterArg, RegisterArg> regMap = new HashMap<RegisterArg, RegisterArg>();
|
||||
private final BlocksPair start;
|
||||
|
||||
private BlocksPair start;
|
||||
private BlocksPair end;
|
||||
|
||||
private int startSplitIndex;
|
||||
private int endSplitIndex;
|
||||
|
||||
private BlockNode startPredecessor;
|
||||
|
||||
public BlocksRemoveInfo(BlocksPair start) {
|
||||
this.start = start;
|
||||
@@ -34,6 +39,18 @@ public final class BlocksRemoveInfo {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(BlocksPair start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public BlocksPair getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(BlocksPair end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public int getStartSplitIndex() {
|
||||
return startSplitIndex;
|
||||
}
|
||||
@@ -42,6 +59,22 @@ public final class BlocksRemoveInfo {
|
||||
this.startSplitIndex = startSplitIndex;
|
||||
}
|
||||
|
||||
public int getEndSplitIndex() {
|
||||
return endSplitIndex;
|
||||
}
|
||||
|
||||
public void setEndSplitIndex(int endSplitIndex) {
|
||||
this.endSplitIndex = endSplitIndex;
|
||||
}
|
||||
|
||||
public void setStartPredecessor(BlockNode startPredecessor) {
|
||||
this.startPredecessor = startPredecessor;
|
||||
}
|
||||
|
||||
public BlockNode getStartPredecessor() {
|
||||
return startPredecessor;
|
||||
}
|
||||
|
||||
public Map<RegisterArg, RegisterArg> getRegMap() {
|
||||
return regMap;
|
||||
}
|
||||
@@ -69,6 +102,7 @@ public final class BlocksRemoveInfo {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BRI start: " + start
|
||||
+ ", end: " + end
|
||||
+ ", list: " + processed
|
||||
+ ", outs: " + outs
|
||||
+ ", regMap: " + regMap
|
||||
|
||||
@@ -878,7 +878,6 @@ public class RegionMaker {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add blocks common for several handlers to some region
|
||||
private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
|
||||
BlockNode start = handler.getHandlerBlock();
|
||||
if (start == null) {
|
||||
|
||||
@@ -12,6 +12,7 @@ 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.InsnList;
|
||||
import jadx.core.utils.InstructionRemover;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -20,6 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -41,10 +43,18 @@ public class SSATransform extends AbstractVisitor {
|
||||
placePhi(mth, i, la);
|
||||
}
|
||||
renameVariables(mth);
|
||||
fixLastTryCatchAssign(mth);
|
||||
if (removeUselessPhi(mth)) {
|
||||
renameVariables(mth);
|
||||
}
|
||||
|
||||
fixLastAssignInTry(mth);
|
||||
removeBlockerInsns(mth);
|
||||
|
||||
boolean repeatFix;
|
||||
int k = 0;
|
||||
do {
|
||||
repeatFix = fixUselessPhi(mth);
|
||||
if (k++ > 50) {
|
||||
throw new JadxRuntimeException("Phi nodes fix limit reached!");
|
||||
}
|
||||
} while (repeatFix);
|
||||
}
|
||||
|
||||
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
|
||||
@@ -65,7 +75,7 @@ public class SSATransform extends AbstractVisitor {
|
||||
for (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) {
|
||||
if (!hasPhi.get(id) && la.isLive(id, regNum)) {
|
||||
BlockNode df = blocks.get(id);
|
||||
addPhi(df, regNum);
|
||||
addPhi(mth, df, regNum);
|
||||
hasPhi.set(id);
|
||||
if (!processed.get(id)) {
|
||||
processed.set(id);
|
||||
@@ -76,19 +86,31 @@ public class SSATransform extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addPhi(BlockNode block, int regNum) {
|
||||
private static void addPhi(MethodNode mth, BlockNode block, int regNum) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
if (phiList == null) {
|
||||
phiList = new PhiListAttr();
|
||||
block.addAttr(phiList);
|
||||
}
|
||||
PhiInsn phiInsn = new PhiInsn(regNum, block.getPredecessors().size());
|
||||
int size = block.getPredecessors().size();
|
||||
if (mth.getEnterBlock() == block) {
|
||||
for (RegisterArg arg : mth.getArguments(true)) {
|
||||
if (arg.getRegNum() == regNum) {
|
||||
size++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PhiInsn phiInsn = new PhiInsn(regNum, size);
|
||||
phiList.getList().add(phiInsn);
|
||||
phiInsn.setOffset(block.getStartOffset());
|
||||
block.getInstructions().add(0, phiInsn);
|
||||
}
|
||||
|
||||
private static void renameVariables(MethodNode mth) {
|
||||
if (!mth.getSVars().isEmpty()) {
|
||||
throw new JadxRuntimeException("SSA rename variables already executed");
|
||||
}
|
||||
int regsCount = mth.getRegsCount();
|
||||
SSAVar[] vars = new SSAVar[regsCount];
|
||||
int[] versions = new int[regsCount];
|
||||
@@ -97,7 +119,25 @@ public class SSATransform extends AbstractVisitor {
|
||||
int regNum = arg.getRegNum();
|
||||
vars[regNum] = mth.makeNewSVar(regNum, versions, arg);
|
||||
}
|
||||
renameVar(mth, vars, versions, mth.getEnterBlock());
|
||||
BlockNode enterBlock = mth.getEnterBlock();
|
||||
initPhiInEnterBlock(vars, enterBlock);
|
||||
renameVar(mth, vars, versions, enterBlock);
|
||||
}
|
||||
|
||||
private static void initPhiInEnterBlock(SSAVar[] vars, BlockNode enterBlock) {
|
||||
PhiListAttr phiList = enterBlock.get(AType.PHI_LIST);
|
||||
if (phiList != null) {
|
||||
for (PhiInsn phiInsn : phiList.getList()) {
|
||||
int regNum = phiInsn.getResult().getRegNum();
|
||||
SSAVar var = vars[regNum];
|
||||
if (var == null) {
|
||||
continue;
|
||||
}
|
||||
RegisterArg arg = phiInsn.bindArg(enterBlock);
|
||||
var.use(arg);
|
||||
var.setUsedInPhi(phiInsn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void renameVar(MethodNode mth, SSAVar[] vars, int[] vers, BlockNode block) {
|
||||
@@ -129,20 +169,14 @@ public class SSATransform extends AbstractVisitor {
|
||||
if (phiList == null) {
|
||||
continue;
|
||||
}
|
||||
int j = s.getPredecessors().indexOf(block);
|
||||
if (j == -1) {
|
||||
throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
|
||||
}
|
||||
for (PhiInsn phiInsn : phiList.getList()) {
|
||||
if (j >= phiInsn.getArgsCount()) {
|
||||
continue;
|
||||
}
|
||||
int regNum = phiInsn.getResult().getRegNum();
|
||||
SSAVar var = vars[regNum];
|
||||
if (var == null) {
|
||||
continue;
|
||||
}
|
||||
var.use(phiInsn.getArg(j));
|
||||
RegisterArg arg = phiInsn.bindArg(block);
|
||||
var.use(arg);
|
||||
var.setUsedInPhi(phiInsn);
|
||||
}
|
||||
}
|
||||
@@ -152,27 +186,58 @@ public class SSATransform extends AbstractVisitor {
|
||||
System.arraycopy(inputVars, 0, vars, 0, vars.length);
|
||||
}
|
||||
|
||||
private static void fixLastTryCatchAssign(MethodNode mth) {
|
||||
/**
|
||||
* Fix last try/catch assign instruction
|
||||
*/
|
||||
private static void fixLastAssignInTry(MethodNode mth) {
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
if (phiList == null || !block.contains(AType.EXC_HANDLER)) {
|
||||
continue;
|
||||
}
|
||||
for (PhiInsn phi : phiList.getList()) {
|
||||
for (int i = 0; i < phi.getArgsCount(); i++) {
|
||||
RegisterArg arg = phi.getArg(i);
|
||||
InsnNode parentInsn = arg.getAssignInsn();
|
||||
if (parentInsn != null
|
||||
&& parentInsn.getResult() != null
|
||||
&& parentInsn.contains(AFlag.TRY_LEAVE)) {
|
||||
phi.removeArg(arg);
|
||||
}
|
||||
if (phiList != null && block.contains(AType.EXC_HANDLER)) {
|
||||
for (PhiInsn phi : phiList.getList()) {
|
||||
fixPhiInTryCatch(phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean removeUselessPhi(MethodNode mth) {
|
||||
private static void fixPhiInTryCatch(PhiInsn phi) {
|
||||
int argsCount = phi.getArgsCount();
|
||||
for (int i = 0; i < argsCount; i++) {
|
||||
RegisterArg arg = phi.getArg(i);
|
||||
InsnNode parentInsn = arg.getAssignInsn();
|
||||
if (parentInsn != null
|
||||
&& parentInsn.getResult() != null
|
||||
&& parentInsn.contains(AFlag.TRY_LEAVE)) {
|
||||
phi.removeArg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean removeBlockerInsns(MethodNode mth) {
|
||||
boolean removed = false;
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
if (phiList == null) {
|
||||
continue;
|
||||
}
|
||||
// check if args must be removed
|
||||
for (PhiInsn phi : phiList.getList()) {
|
||||
for (int i = 0; i < phi.getArgsCount(); i++) {
|
||||
RegisterArg arg = phi.getArg(i);
|
||||
InsnNode parentInsn = arg.getAssignInsn();
|
||||
if (parentInsn != null && parentInsn.contains(AFlag.REMOVE)) {
|
||||
phi.removeArg(arg);
|
||||
InstructionRemover.remove(mth, block, parentInsn);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static boolean fixUselessPhi(MethodNode mth) {
|
||||
boolean changed = false;
|
||||
List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
|
||||
for (SSAVar var : mth.getSVars()) {
|
||||
// phi result not used
|
||||
@@ -180,6 +245,7 @@ public class SSATransform extends AbstractVisitor {
|
||||
InsnNode assignInsn = var.getAssign().getParentInsn();
|
||||
if (assignInsn != null && assignInsn.getType() == InsnType.PHI) {
|
||||
insnToRemove.add((PhiInsn) assignInsn);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,41 +254,53 @@ public class SSATransform extends AbstractVisitor {
|
||||
if (phiList == null) {
|
||||
continue;
|
||||
}
|
||||
for (PhiInsn phi : phiList.getList()) {
|
||||
removePhiWithSameArgs(phi, insnToRemove);
|
||||
Iterator<PhiInsn> it = phiList.getList().iterator();
|
||||
while (it.hasNext()) {
|
||||
PhiInsn phi = it.next();
|
||||
if (fixPhiWithSameArgs(mth, block, phi)) {
|
||||
it.remove();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return removePhiList(mth, insnToRemove);
|
||||
removePhiList(mth, insnToRemove);
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void removePhiWithSameArgs(PhiInsn phi, List<PhiInsn> insnToRemove) {
|
||||
if (phi.getArgsCount() <= 1) {
|
||||
insnToRemove.add(phi);
|
||||
return;
|
||||
private static boolean fixPhiWithSameArgs(MethodNode mth, BlockNode block, PhiInsn phi) {
|
||||
if (phi.getArgsCount() == 0) {
|
||||
for (RegisterArg useArg : phi.getResult().getSVar().getUseList()) {
|
||||
InsnNode useInsn = useArg.getParentInsn();
|
||||
if (useInsn != null && useInsn.getType() == InsnType.PHI) {
|
||||
phi.removeArg(useArg);
|
||||
}
|
||||
}
|
||||
InstructionRemover.remove(mth, block, phi);
|
||||
return true;
|
||||
}
|
||||
boolean allSame = phi.getArgsCount() == 1 || isSameArgs(phi);
|
||||
if (!allSame) {
|
||||
return false;
|
||||
}
|
||||
return replacePhiWithMove(mth, block, phi, phi.getArg(0));
|
||||
}
|
||||
|
||||
private static boolean isSameArgs(PhiInsn phi) {
|
||||
boolean allSame = true;
|
||||
SSAVar var = phi.getArg(0).getSVar();
|
||||
for (int i = 1; i < phi.getArgsCount(); i++) {
|
||||
if (var != phi.getArg(i).getSVar()) {
|
||||
SSAVar var = null;
|
||||
for (int i = 0; i < phi.getArgsCount(); i++) {
|
||||
RegisterArg arg = phi.getArg(i);
|
||||
if (var == null) {
|
||||
var = arg.getSVar();
|
||||
} else if (var != arg.getSVar()) {
|
||||
allSame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allSame) {
|
||||
// replace
|
||||
insnToRemove.add(phi);
|
||||
SSAVar assign = phi.getResult().getSVar();
|
||||
for (RegisterArg arg : new ArrayList<RegisterArg>(assign.getUseList())) {
|
||||
assign.removeUse(arg);
|
||||
var.use(arg);
|
||||
}
|
||||
}
|
||||
return allSame;
|
||||
}
|
||||
|
||||
private static boolean removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {
|
||||
if (insnToRemove.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (BlockNode block : mth.getBasicBlocks()) {
|
||||
PhiListAttr phiList = block.get(AType.PHI_LIST);
|
||||
if (phiList == null) {
|
||||
@@ -232,6 +310,9 @@ public class SSATransform extends AbstractVisitor {
|
||||
for (PhiInsn phiInsn : insnToRemove) {
|
||||
if (list.remove(phiInsn)) {
|
||||
for (InsnArg arg : phiInsn.getArguments()) {
|
||||
if (arg == null) {
|
||||
continue;
|
||||
}
|
||||
SSAVar sVar = ((RegisterArg) arg).getSVar();
|
||||
if (sVar != null) {
|
||||
sVar.setUsedInPhi(null);
|
||||
@@ -247,4 +328,67 @@ public class SSATransform extends AbstractVisitor {
|
||||
insnToRemove.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean replacePhiWithMove(MethodNode mth, BlockNode block, PhiInsn phi, RegisterArg arg) {
|
||||
List<InsnNode> insns = block.getInstructions();
|
||||
int phiIndex = InsnList.getIndex(insns, phi);
|
||||
if (phiIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
SSAVar assign = phi.getResult().getSVar();
|
||||
SSAVar argVar = arg.getSVar();
|
||||
if (argVar != null) {
|
||||
argVar.removeUse(arg);
|
||||
argVar.setUsedInPhi(null);
|
||||
}
|
||||
// try inline
|
||||
if (inlinePhiInsn(mth, block, phi)) {
|
||||
insns.remove(phiIndex);
|
||||
} else {
|
||||
assign.setUsedInPhi(null);
|
||||
|
||||
InsnNode m = new InsnNode(InsnType.MOVE, 1);
|
||||
m.add(AFlag.SYNTHETIC);
|
||||
m.setResult(phi.getResult());
|
||||
m.addArg(arg);
|
||||
arg.getSVar().use(arg);
|
||||
insns.set(phiIndex, m);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean inlinePhiInsn(MethodNode mth, BlockNode block, PhiInsn phi) {
|
||||
SSAVar resVar = phi.getResult().getSVar();
|
||||
if (resVar == null) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg arg = phi.getArg(0);
|
||||
if (arg.getSVar() == null) {
|
||||
return false;
|
||||
}
|
||||
List<RegisterArg> useList = resVar.getUseList();
|
||||
for (RegisterArg useArg : new ArrayList<RegisterArg>(useList)) {
|
||||
InsnNode useInsn = useArg.getParentInsn();
|
||||
if (useInsn == null || useInsn == phi) {
|
||||
return false;
|
||||
}
|
||||
useArg.getSVar().removeUse(useArg);
|
||||
RegisterArg inlArg = arg.duplicate();
|
||||
if (!useInsn.replaceArg(useArg, inlArg)) {
|
||||
return false;
|
||||
}
|
||||
inlArg.getSVar().use(inlArg);
|
||||
inlArg.setName(useArg.getName());
|
||||
inlArg.setType(useArg.getType());
|
||||
}
|
||||
if (block.contains(AType.EXC_HANDLER)) {
|
||||
// don't inline into exception handler
|
||||
InsnNode assignInsn = arg.getAssignInsn();
|
||||
if (assignInsn != null) {
|
||||
assignInsn.add(AFlag.DONT_INLINE);
|
||||
}
|
||||
}
|
||||
InstructionRemover.unbindInsn(mth, phi);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,10 @@ public class TypeInference extends AbstractVisitor {
|
||||
for (int i = 0; i < phi.getArgsCount(); i++) {
|
||||
RegisterArg arg = phi.getArg(i);
|
||||
arg.setType(type);
|
||||
arg.getSVar().setName(phi.getResult().getName());
|
||||
SSAVar sVar = arg.getSVar();
|
||||
if (sVar != null) {
|
||||
sVar.setName(phi.getResult().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,11 @@ public final class InsnList implements Iterable<InsnNode> {
|
||||
}
|
||||
|
||||
public static int getIndex(List<InsnNode> list, InsnNode insn) {
|
||||
int i = 0;
|
||||
for (InsnNode curObj : list) {
|
||||
if (curObj == insn) {
|
||||
int size = list.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (list.get(i) == insn) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -63,14 +65,38 @@ public class InstructionRemover {
|
||||
}
|
||||
|
||||
public static void unbindInsn(MethodNode mth, InsnNode insn) {
|
||||
unbindResult(mth, insn);
|
||||
for (InsnArg arg : insn.getArguments()) {
|
||||
unbindArgUsage(mth, arg);
|
||||
}
|
||||
if (insn.getType() == InsnType.PHI) {
|
||||
for (InsnArg arg : insn.getArguments()) {
|
||||
if (arg instanceof RegisterArg) {
|
||||
fixUsedInPhiFlag((RegisterArg) arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
insn.add(AFlag.INCONSISTENT_CODE);
|
||||
}
|
||||
|
||||
public static void fixUsedInPhiFlag(RegisterArg useReg) {
|
||||
PhiInsn usedIn = null;
|
||||
for (RegisterArg reg : useReg.getSVar().getUseList()) {
|
||||
InsnNode parentInsn = reg.getParentInsn();
|
||||
if (parentInsn != null
|
||||
&& parentInsn.getType() == InsnType.PHI
|
||||
&& parentInsn.containsArg(useReg)) {
|
||||
usedIn = (PhiInsn) parentInsn;
|
||||
}
|
||||
}
|
||||
useReg.getSVar().setUsedInPhi(usedIn);
|
||||
}
|
||||
|
||||
public static void unbindResult(MethodNode mth, InsnNode insn) {
|
||||
RegisterArg r = insn.getResult();
|
||||
if (r != null && r.getSVar() != null) {
|
||||
mth.removeSVar(r.getSVar());
|
||||
}
|
||||
for (InsnArg arg : insn.getArguments()) {
|
||||
unbindArgUsage(mth, arg);
|
||||
}
|
||||
insn.add(AFlag.INCONSISTENT_CODE);
|
||||
}
|
||||
|
||||
public static void unbindArgUsage(MethodNode mth, InsnArg arg) {
|
||||
@@ -122,6 +148,9 @@ public class InstructionRemover {
|
||||
}
|
||||
|
||||
public static void removeAll(MethodNode mth, BlockNode block, List<InsnNode> insns) {
|
||||
if (insns.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
removeAll(mth, block.getInstructions(), insns);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,6 @@ public class ManifestAttributes {
|
||||
return sb.deleteCharAt(sb.length() - 1).toString();
|
||||
}
|
||||
}
|
||||
return "UNKNOWN_DATA_" + Integer.toHexString(value);
|
||||
return "UNKNOWN_DATA_0x" + Integer.toHexString(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public class TestArgInline extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public void method(int a) {
|
||||
public void test(int a) {
|
||||
while (a < 10) {
|
||||
int b = a + 1;
|
||||
a = b;
|
||||
|
||||
@@ -52,7 +52,7 @@ public class TestContinueInLoop2 extends IntegrationTest {
|
||||
TryCatchBlock catchBlock = catchAttr.getTryBlock();
|
||||
if (handlerBlock != catchBlock) {
|
||||
handlerBlock.merge(mth, catchBlock);
|
||||
catchBlock.removeInsn(insn);
|
||||
catchBlock.removeInsn(mth, insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
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.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestFinally extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
private static final String DISPLAY_NAME = "name";
|
||||
|
||||
String test(Context context, Object uri) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String[] projection = {DISPLAY_NAME};
|
||||
cursor = context.query(uri, projection);
|
||||
int columnIndex = cursor.getColumnIndexOrThrow(DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(columnIndex);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Context {
|
||||
public Cursor query(Object o, String[] s) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class Cursor {
|
||||
public void close() {
|
||||
}
|
||||
|
||||
public void moveToFirst() {
|
||||
}
|
||||
|
||||
public int getColumnIndexOrThrow(String s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getString(int i) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("cursor.getString(columnIndex);"));
|
||||
assertThat(code, not(containsOne("String str = true;")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package jadx.tests.integration.trycatch;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestFinally2 extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public Result test(byte[] data) throws IOException {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = getInputStream(data);
|
||||
decode(inputStream);
|
||||
return new Result(400);
|
||||
} finally {
|
||||
closeQuietly(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Result {
|
||||
private final int mCode;
|
||||
|
||||
public Result(int code) {
|
||||
mCode = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return mCode;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getInputStream(byte[] data) throws IOException {
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
private int decode(InputStream inputStream) throws IOException {
|
||||
return inputStream.available();
|
||||
}
|
||||
|
||||
private void closeQuietly(InputStream is) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("decode(inputStream);"));
|
||||
// TODO
|
||||
// assertThat(code, not(containsOne("result =")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user