diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 493559fb3..e79e1de9a 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -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()); diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 4f23e1e22..efd6f9243 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -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"; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java index d25134921..753ce9f2c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/PhiInsn.java @@ -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 blockBinds; public PhiInsn(int regNum, int predecessors) { super(InsnType.PHI, predecessors); + this.blockBinds = new IdentityHashMap(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 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; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index 0c79d9f82..d51fbf182 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -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 diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index 7a1588e8d..a9dfe94a7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -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; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java index 96caf0476..12cd9250c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java +++ b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java @@ -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 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 getInsns() { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java similarity index 97% rename from jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java rename to jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java index 77c13a457..009cd6c54 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlineVisitor.java @@ -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); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java index 63c177726..6ca8ef972 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/FallbackModeVisitor.java @@ -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: diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java index b54c4d943..27a249c56 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockFinallyExtract.java @@ -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 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 blocks = new ArrayList(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 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 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 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 removes) { + for (BlocksRemoveInfo removeInfo : removes) { + if (!removeInfo.getRegMap().isEmpty()) { + return true; + } + } + return false; + } + private static BlocksRemoveInfo removeInsns(MethodNode mth, BlockNode remBlock, List 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 baseCS = startBlock.getCleanSuccessors(); List 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 first = remBlock.getInstructions(); - List second = startBlock.getInstructions(); - if (first.size() != second.size()) { + List 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 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 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 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(block.getSuccessors())) { + removeConnection(block, s); + connect(newBlock, s); + } + block.getSuccessors().clear(); + connect(block, newBlock); + block.updateCleanSuccessors(); + newBlock.updateCleanSuccessors(); + + List 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 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); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java index eb3baed9b..a9cc67b11 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/BlockProcessor.java @@ -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(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/helpers/BlocksRemoveInfo.java b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/helpers/BlocksRemoveInfo.java index 78d48bc87..5e6d46871 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/helpers/BlocksRemoveInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/blocksmaker/helpers/BlocksRemoveInfo.java @@ -14,9 +14,14 @@ public final class BlocksRemoveInfo { private final Set processed = new HashSet(); private final Set outs = new HashSet(); private final Map regMap = new HashMap(); - 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 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 diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index 0457b0621..7a0e652ba 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -878,7 +878,6 @@ public class RegionMaker { } } - // TODO add blocks common for several handlers to some region private void processExcHandler(ExceptionHandler handler, Set exits) { BlockNode start = handler.getHandlerBlock(); if (start == null) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java index 90d360d14..ca2627190 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ssa/SSATransform.java @@ -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 insnToRemove = new ArrayList(); 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 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 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(assign.getUseList())) { - assign.removeUse(arg); - var.use(arg); - } - } + return allSame; } private static boolean removePhiList(MethodNode mth, List 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 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 useList = resVar.getUseList(); + for (RegisterArg useArg : new ArrayList(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; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java index 8e36ac74d..f244b7bee 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeInference.java @@ -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()); + } } } diff --git a/jadx-core/src/main/java/jadx/core/utils/InsnList.java b/jadx-core/src/main/java/jadx/core/utils/InsnList.java index faafb5201..5c8612f68 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InsnList.java +++ b/jadx-core/src/main/java/jadx/core/utils/InsnList.java @@ -29,12 +29,11 @@ public final class InsnList implements Iterable { } public static int getIndex(List 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; } diff --git a/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java b/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java index ecd8d1e41..3c687930d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java +++ b/jadx-core/src/main/java/jadx/core/utils/InstructionRemover.java @@ -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 insns) { + if (insns.isEmpty()) { + return; + } removeAll(mth, block.getInstructions(), insns); } diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java b/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java index a6388bc4b..1438d7681 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java @@ -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); } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/TestArgInline.java b/jadx-core/src/test/java/jadx/tests/integration/TestArgInline.java index b1162934a..93add0f77 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/TestArgInline.java +++ b/jadx-core/src/test/java/jadx/tests/integration/TestArgInline.java @@ -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; diff --git a/jadx-core/src/test/java/jadx/tests/integration/loops/TestContinueInLoop2.java b/jadx-core/src/test/java/jadx/tests/integration/loops/TestContinueInLoop2.java index c73e3ee65..bc225481d 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/loops/TestContinueInLoop2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/loops/TestContinueInLoop2.java @@ -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); } } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally.java new file mode 100644 index 000000000..c3e0879bf --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally.java @@ -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;"))); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally2.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally2.java new file mode 100644 index 000000000..898067e46 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestFinally2.java @@ -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 ="))); + } +}