Commit c8923950 authored by Skylot's avatar Skylot

fix: redone finally extract

parent 4ce5cc84
......@@ -20,6 +20,8 @@ import jadx.core.dex.visitors.EnumVisitor;
import jadx.core.dex.visitors.ExtractFieldInit;
import jadx.core.dex.visitors.FallbackModeVisitor;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.dex.visitors.MarkFinallyVisitor;
import jadx.core.dex.visitors.MethodInlineVisitor;
import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.PrepareForCodeGen;
......@@ -27,19 +29,18 @@ import jadx.core.dex.visitors.ReSugarCode;
import jadx.core.dex.visitors.RenameVisitor;
import jadx.core.dex.visitors.SimplifyVisitor;
import jadx.core.dex.visitors.blocksmaker.BlockExceptionHandler;
import jadx.core.dex.visitors.blocksmaker.BlockFinallyExtract;
import jadx.core.dex.visitors.blocksmaker.BlockFinish;
import jadx.core.dex.visitors.blocksmaker.BlockProcessor;
import jadx.core.dex.visitors.blocksmaker.BlockSplitter;
import jadx.core.dex.visitors.debuginfo.DebugInfoApplyVisitor;
import jadx.core.dex.visitors.debuginfo.DebugInfoParseVisitor;
import jadx.core.dex.visitors.regions.CheckRegions;
import jadx.core.dex.visitors.regions.CleanRegions;
import jadx.core.dex.visitors.regions.IfRegionVisitor;
import jadx.core.dex.visitors.regions.LoopRegionVisitor;
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
import jadx.core.dex.visitors.regions.ReturnVisitor;
import jadx.core.dex.visitors.regions.variables.ProcessVariables;
import jadx.core.dex.visitors.ssa.EliminatePhiNodes;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;
......@@ -68,14 +69,14 @@ public class Jadx {
passes.add(new BlockProcessor());
passes.add(new BlockExceptionHandler());
passes.add(new BlockFinallyExtract());
passes.add(new BlockFinish());
passes.add(new SSATransform());
passes.add(new ConstructorVisitor());
passes.add(new InitCodeVariables());
passes.add(new MarkFinallyVisitor());
passes.add(new ConstInlineVisitor());
passes.add(new TypeInferenceVisitor());
passes.add(new EliminatePhiNodes());
passes.add(new DebugInfoApplyVisitor());
passes.add(new ModVisitor());
......@@ -88,6 +89,7 @@ public class Jadx {
passes.add(new RegionMakerVisitor());
passes.add(new IfRegionVisitor());
passes.add(new ReturnVisitor());
passes.add(new CleanRegions());
passes.add(new CodeShrinker());
passes.add(new SimplifyVisitor());
......
......@@ -499,7 +499,6 @@ public class InsnGen {
break;
case PHI:
case MERGE:
fallbackOnlyInsn(insn);
code.add(insn.getType().toString()).add("(");
for (InsnArg insnArg : insn.getArguments()) {
......
......@@ -19,6 +19,8 @@ import jadx.core.dex.instructions.args.NamedArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.instructions.mods.ConstructorInsn;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.StringUtils;
......@@ -55,6 +57,17 @@ public class NameGen {
public NameGen(MethodNode mth, boolean fallback) {
this.mth = mth;
this.fallback = fallback;
addNamesUsedInClass();
}
private void addNamesUsedInClass() {
ClassNode parentClass = mth.getParentClass();
for (FieldNode field : parentClass.getFields()) {
varNames.add(field.getAlias());
}
for (ClassNode innerClass : parentClass.getInnerClasses()) {
varNames.add(innerClass.getAlias().getShortName());
}
}
public String assignArg(CodeVar var) {
......
......@@ -3,6 +3,7 @@ package jadx.core.codegen;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -36,6 +37,7 @@ import jadx.core.dex.regions.loops.ForLoop;
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.regions.loops.LoopType;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.CodegenException;
......@@ -98,6 +100,10 @@ public class RegionGen extends InsnGen {
}
private void makeSimpleBlock(IBlock block, CodeWriter code) throws CodegenException {
if (block.contains(AFlag.DONT_GENERATE)) {
return;
}
for (InsnNode insn : block.getInstructions()) {
if (!insn.contains(AFlag.DONT_GENERATE)) {
makeInsn(insn, code);
......@@ -233,7 +239,8 @@ public class RegionGen extends InsnGen {
}
private CodeWriter makeSwitch(SwitchRegion sw, CodeWriter code) throws CodegenException {
SwitchNode insn = (SwitchNode) sw.getHeader().getInstructions().get(0);
SwitchNode insn = (SwitchNode) BlockUtils.getLastInsn(sw.getHeader());
Objects.requireNonNull(insn, "Switch insn not found in header");
InsnArg arg = insn.getArg(0);
code.startLine("switch (");
addArg(code, arg, false);
......
......@@ -8,26 +8,28 @@ public enum AFlag {
LOOP_END,
SYNTHETIC,
FINAL, // SSAVar attribute for make var final
RETURN, // block contains only return instruction
ORIG_RETURN,
DECLARE_VAR,
DONT_WRAP,
DONT_INLINE,
DONT_GENERATE, // process as usual, but don't output to generated code
REMOVE, // can be completely removed
ADDED_TO_REGION,
FINALLY_INSNS,
SKIP_FIRST_ARG,
SKIP_ARG, // skip argument in invoke call
ANONYMOUS_CONSTRUCTOR,
ANONYMOUS_CLASS,
THIS,
METHOD_ARGUMENT, // RegisterArg attribute for method arguments
CUSTOM_DECLARE, // variable for this register don't need declaration
DECLARE_VAR,
ELSE_IF_CHAIN,
......
......@@ -27,6 +27,9 @@ public class PhiListAttr implements IAttribute {
for (PhiInsn phiInsn : list) {
sb.append('r').append(phiInsn.getResult().getRegNum()).append(" ");
}
for (PhiInsn phiInsn : list) {
sb.append("\n ").append(phiInsn).append(" ").append(phiInsn.getAttributesString());
}
return sb.toString();
}
}
......@@ -30,7 +30,7 @@ public class RegDebugInfoAttr implements IAttribute {
}
@Override
public AType getType() {
public AType<RegDebugInfoAttr> getType() {
return AType.REG_DEBUG_INFO;
}
......
......@@ -15,35 +15,38 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
public final class FillArrayNode extends InsnNode {
private static final ArgType ONE_BYTE_TYPE = ArgType.unknown(PrimitiveType.BOOLEAN, PrimitiveType.BYTE);
private static final ArgType TWO_BYTES_TYPE = ArgType.unknown(PrimitiveType.SHORT, PrimitiveType.CHAR);
private static final ArgType FOUR_BYTES_TYPE = ArgType.unknown(PrimitiveType.INT, PrimitiveType.FLOAT);
private static final ArgType EIGHT_BYTES_TYPE = ArgType.unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE);
private final Object data;
private final int size;
private ArgType elemType;
public FillArrayNode(int resReg, FillArrayDataPayloadDecodedInstruction payload) {
super(InsnType.FILL_ARRAY, 1);
ArgType elType;
switch (payload.getElementWidthUnit()) {
ArgType elType = getElementType(payload.getElementWidthUnit());
addArg(InsnArg.reg(resReg, ArgType.array(elType)));
this.data = payload.getData();
this.size = payload.getSize();
this.elemType = elType;
}
private static ArgType getElementType(short elementWidthUnit) {
switch (elementWidthUnit) {
case 1:
elType = ArgType.unknown(PrimitiveType.BOOLEAN, PrimitiveType.BYTE);
break;
return ONE_BYTE_TYPE;
case 2:
elType = ArgType.unknown(PrimitiveType.SHORT, PrimitiveType.CHAR);
break;
return TWO_BYTES_TYPE;
case 4:
elType = ArgType.unknown(PrimitiveType.INT, PrimitiveType.FLOAT);
break;
return FOUR_BYTES_TYPE;
case 8:
elType = ArgType.unknown(PrimitiveType.LONG, PrimitiveType.DOUBLE);
break;
return EIGHT_BYTES_TYPE;
default:
throw new JadxRuntimeException("Unknown array element width: " + payload.getElementWidthUnit());
throw new JadxRuntimeException("Unknown array element width: " + elementWidthUnit);
}
addArg(InsnArg.reg(resReg, ArgType.array(elType)));
this.data = payload.getData();
this.size = payload.getSize();
this.elemType = elType;
}
public Object getData() {
......
......@@ -19,7 +19,6 @@ import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode;
......@@ -405,12 +404,10 @@ public class InsnDecoder {
return new GotoNode(insn.getTarget());
case Opcodes.THROW:
return insn(InsnType.THROW, null,
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)));
return insn(InsnType.THROW, null, InsnArg.reg(insn, 0, ArgType.THROWABLE));
case Opcodes.MOVE_EXCEPTION:
return insn(InsnType.MOVE_EXCEPTION,
InsnArg.reg(insn, 0, ArgType.unknown(PrimitiveType.OBJECT)));
return insn(InsnType.MOVE_EXCEPTION, InsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT_NO_ARRAY));
case Opcodes.RETURN_VOID:
return new InsnNode(InsnType.RETURN, 0);
......
......@@ -66,9 +66,6 @@ public enum InsnType {
ONE_ARG,
PHI,
// merge all arguments in one
MERGE,
// TODO: now multidimensional arrays created using Array.newInstance function
NEW_MULTIDIM_ARRAY
}
......@@ -24,6 +24,7 @@ public final class PhiInsn extends InsnNode {
this.blockBinds = new LinkedHashMap<>(predecessors);
setResult(InsnArg.reg(regNum, ArgType.UNKNOWN));
add(AFlag.DONT_INLINE);
add(AFlag.DONT_GENERATE);
}
public RegisterArg bindArg(BlockNode pred) {
......
......@@ -30,6 +30,8 @@ public abstract class ArgType {
public static final ArgType UNKNOWN = unknown(PrimitiveType.values());
public static final ArgType UNKNOWN_OBJECT = unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY);
public static final ArgType UNKNOWN_OBJECT_NO_ARRAY = unknown(PrimitiveType.OBJECT);
public static final ArgType UNKNOWN_ARRAY = array(UNKNOWN);
public static final ArgType NARROW = unknown(
PrimitiveType.INT, PrimitiveType.FLOAT,
......
package jadx.core.dex.instructions.args;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CodeVar {
private String name;
private ArgType type;
private List<SSAVar> ssaVars;
private List<SSAVar> ssaVars = new ArrayList<>(3);
private boolean isFinal;
private boolean isThis;
......@@ -42,14 +43,16 @@ public class CodeVar {
return ssaVars;
}
public void setSsaVars(List<SSAVar> ssaVars) {
if (ssaVars.size() == 1) {
this.ssaVars = Collections.singletonList(ssaVars.get(0));
} else {
this.ssaVars = ssaVars;
public void addSsaVar(SSAVar ssaVar) {
if (!ssaVars.contains(ssaVar)) {
ssaVars.add(ssaVar);
}
}
public void setSsaVars(List<SSAVar> ssaVars) {
this.ssaVars = ssaVars;
}
public boolean isFinal() {
return isFinal;
}
......
......@@ -139,6 +139,7 @@ public class SSAVar extends AttrNode {
public void setCodeVar(@NotNull CodeVar codeVar) {
this.codeVar = codeVar;
codeVar.addSsaVar(this);
}
public boolean isCodeVarSet() {
......
......@@ -15,6 +15,7 @@ import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
public final class IfCondition {
......@@ -54,10 +55,11 @@ public final class IfCondition {
}
public static IfCondition fromIfBlock(BlockNode header) {
if (header == null) {
InsnNode lastInsn = BlockUtils.getLastInsn(header);
if (lastInsn == null) {
return null;
}
return fromIfNode((IfNode) header.getInstructions().get(0));
return fromIfNode((IfNode) lastInsn);
}
public static IfCondition fromIfNode(IfNode insn) {
......
......@@ -8,8 +8,9 @@ import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBranchRegion;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.regions.AbstractRegion;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.BlockUtils;
public final class IfRegion extends AbstractRegion implements IBranchRegion {
......@@ -21,9 +22,6 @@ public final class IfRegion extends AbstractRegion implements IBranchRegion {
public IfRegion(IRegion parent, BlockNode header) {
super(parent);
if (header.getInstructions().size() != 1) {
throw new JadxRuntimeException("Expected only one instruction in 'if' header");
}
this.header = header;
this.condition = IfCondition.fromIfBlock(header);
}
......@@ -74,10 +72,8 @@ public final class IfRegion extends AbstractRegion implements IBranchRegion {
}
public int getSourceLine() {
if (header.getInstructions().isEmpty()) {
return 0;
}
return header.getInstructions().get(0).getSourceLine();
InsnNode lastInsn = BlockUtils.getLastInsn(header);
return lastInsn == null ? 0 : lastInsn.getSourceLine();
}
@Override
......@@ -130,6 +126,6 @@ public final class IfRegion extends AbstractRegion implements IBranchRegion {
@Override
public String toString() {
return "IF " + header + " then (" + thenRegion + ") else (" + elseRegion + ")";
return "IF " + header + " THEN:" + thenRegion + " ELSE:" + elseRegion;
}
}
......@@ -15,6 +15,7 @@ import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.regions.AbstractRegion;
import jadx.core.dex.regions.conditions.IfCondition;
import jadx.core.utils.BlockUtils;
public final class LoopRegion extends AbstractRegion {
......@@ -76,7 +77,7 @@ public final class LoopRegion extends AbstractRegion {
}
private IfNode getIfInsn() {
return (IfNode) conditionBlock.getInstructions().get(0);
return (IfNode) BlockUtils.getLastInsn(conditionBlock);
}
/**
......@@ -132,13 +133,8 @@ public final class LoopRegion extends AbstractRegion {
}
public int getConditionSourceLine() {
if (conditionBlock != null) {
List<InsnNode> condInsns = conditionBlock.getInstructions();
if (!condInsns.isEmpty()) {
return condInsns.get(0).getSourceLine();
}
}
return 0;
InsnNode lastInsn = BlockUtils.getLastInsn(conditionBlock);
return lastInsn == null ? 0 : lastInsn.getSourceLine();
}
public LoopType getType() {
......
......@@ -223,6 +223,14 @@ public class CodeShrinker extends AbstractVisitor {
if (assignInsn == null || assignInsn.contains(AFlag.DONT_INLINE)) {
continue;
}
List<RegisterArg> useList = sVar.getUseList();
if (!useList.isEmpty()) {
InsnNode parentInsn = useList.get(0).getParentInsn();
if (parentInsn != null && parentInsn.contains(AFlag.DONT_GENERATE)) {
continue;
}
}
int assignPos = insnList.getIndex(assignInsn);
if (assignPos != -1) {
WrapInfo wrapInfo = argsInfo.checkInline(assignPos, arg);
......
......@@ -27,7 +27,10 @@ import jadx.core.utils.exceptions.JadxOverflowException;
@JadxVisitor(
name = "Constants Inline",
desc = "Inline constant registers into instructions",
runAfter = SSATransform.class,
runAfter = {
SSATransform.class,
MarkFinallyVisitor.class
},
runBefore = TypeInferenceVisitor.class
)
public class ConstInlineVisitor extends AbstractVisitor {
......@@ -48,7 +51,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
}
private static void checkInsn(MethodNode mth, InsnNode insn, List<InsnNode> toRemove) {
if (insn.contains(AFlag.DONT_INLINE)) {
if (insn.contains(AFlag.DONT_INLINE) || insn.contains(AFlag.DONT_GENERATE)) {
return;
}
InsnType insnType = insn.getType();
......@@ -71,9 +74,36 @@ public class ConstInlineVisitor extends AbstractVisitor {
}
return;
}
if (checkForFinallyBlock(sVar)) {
return;
}
// all check passed, run replace
replaceConst(mth, insn, lit, toRemove);
}
private static boolean checkForFinallyBlock(SSAVar sVar) {
List<SSAVar> ssaVars = sVar.getCodeVar().getSsaVars();
if (ssaVars.size() <= 1) {
return false;
}
int countInsns = 0;
int countFinallyInsns = 0;
for (SSAVar ssaVar : ssaVars) {
for (RegisterArg reg : ssaVar.getUseList()) {
InsnNode parentInsn = reg.getParentInsn();
if (parentInsn != null) {
countInsns++;
if (parentInsn.contains(AFlag.FINALLY_INSNS)) {
countFinallyInsns++;
}
}
}
}
return countFinallyInsns != 0 && countFinallyInsns != countInsns;
}
/**
* Don't inline null object if:
* - used as instance arg in invoke instruction
......@@ -101,7 +131,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
return false;
}
private static void replaceConst(MethodNode mth, InsnNode constInsn, long literal, List<InsnNode> toRemove) {
private static int replaceConst(MethodNode mth, InsnNode constInsn, long literal, List<InsnNode> toRemove) {
SSAVar ssaVar = constInsn.getResult().getSVar();
List<RegisterArg> useList = new ArrayList<>(ssaVar.getUseList());
int replaceCount = 0;
......@@ -113,6 +143,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
if (replaceCount == useList.size()) {
toRemove.add(constInsn);
}
return replaceCount;
}
private static boolean replaceArg(MethodNode mth, RegisterArg arg, long literal, InsnNode constInsn, List<InsnNode> toRemove) {
......@@ -121,7 +152,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
return false;
}
InsnType insnType = useInsn.getType();
if (insnType == InsnType.PHI || insnType == InsnType.MERGE) {
if (insnType == InsnType.PHI) {
return false;
}
ArgType argType = arg.getInitType();
......
......@@ -185,9 +185,9 @@ public class DotGraphVisitor extends AbstractVisitor {
dot.add("}\"];");
BlockNode falsePath = null;
List<InsnNode> list = block.getInstructions();
if (!list.isEmpty() && list.get(0).getType() == InsnType.IF) {
falsePath = ((IfNode) list.get(0)).getElseBlock();
InsnNode lastInsn = BlockUtils.getLastInsn(block);
if (lastInsn != null && lastInsn.getType() == InsnType.IF) {
falsePath = ((IfNode) lastInsn).getElseBlock();
}
for (BlockNode next : block.getSuccessors()) {
String style = next == falsePath ? "[style=dashed]" : "";
......
package jadx.core.dex.visitors.ssa;
package jadx.core.dex.visitors;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.CodeVar;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.BlockUtils;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class EliminatePhiNodes extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(EliminatePhiNodes.class);
@JadxVisitor(
name = "InitCodeVariables",
desc = "Initialize code variables",
runAfter = SSATransform.class
)
public class InitCodeVariables extends AbstractVisitor {
@Override
public void visit(MethodNode mth) throws JadxException {
if (mth.isNoCode()) {
return;
}
replaceMergeInstructions(mth);
removePhiInstructions(mth);
initCodeVars(mth);
}
private static void removePhiInstructions(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
PhiListAttr phiList = block.get(AType.PHI_LIST);
if (phiList == null) {
continue;
}
List<PhiInsn> list = phiList.getList();
for (PhiInsn phiInsn : list) {
removeInsn(mth, block, phiInsn);
}
}
}
private static void removeInsn(MethodNode mth, BlockNode block, PhiInsn phiInsn) {
Iterator<InsnNode> it = block.getInstructions().iterator();
while (it.hasNext()) {
InsnNode insn = it.next();
if (insn == phiInsn) {
it.remove();
return;
}
}
LOG.warn("Phi node not removed: {}, mth: {}", phiInsn, mth);
phiInsn.add(AFlag.DONT_GENERATE);
}
private void replaceMergeInstructions(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
List<InsnNode> insns = block.getInstructions();
if (insns.isEmpty()) {
continue;
}
InsnNode insn = insns.get(0);
if (insn.getType() == InsnType.MERGE) {
replaceMerge(mth, block, insn);
}
}
}
/**
* Replace 'MERGE' with 'PHI' insn.
*/
private void replaceMerge(MethodNode mth, BlockNode block, InsnNode insn) {
if (insn.getArgsCount() != 2) {
throw new JadxRuntimeException("Unexpected count of arguments in merge insn: " + insn);
}
RegisterArg oldArg = (RegisterArg) insn.getArg(1);
RegisterArg newArg = (RegisterArg) insn.getArg(0);
int newRegNum = newArg.getRegNum();
if (oldArg.getRegNum() == newRegNum) {
throw new JadxRuntimeException("Unexpected register number in merge insn: " + insn);
}
SSAVar oldSVar = oldArg.getSVar();
RegisterArg assignArg = oldSVar.getAssign();
InsnNode assignParentInsn = assignArg.getParentInsn();
BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignParentInsn);
if (assignBlock == null) {
throw new JadxRuntimeException("Unknown assign block for " + assignParentInsn);
}
BlockNode assignPred = null;
for (BlockNode pred : block.getPredecessors()) {
if (BlockUtils.isPathExists(assignBlock, pred)) {
assignPred = pred;
break;
}
}
if (assignPred == null) {
throw new JadxRuntimeException("Assign predecessor not found for " + assignBlock + " from " + block);
}
// all checks passed
RegisterArg newAssignArg = oldArg.duplicate(newRegNum, null);
SSAVar newSVar = mth.makeNewSVar(newRegNum, newAssignArg);
newSVar.setName(oldSVar.getName());
mth.root().getTypeUpdate().apply(newSVar, assignArg.getType());
if (assignParentInsn != null) {
assignParentInsn.setResult(newAssignArg);
}
for (RegisterArg useArg : new ArrayList<>(oldSVar.getUseList())) {
RegisterArg newUseArg = useArg.duplicate(newRegNum, newSVar);
InsnNode parentInsn = useArg.getParentInsn();
if (parentInsn != null) {
newSVar.use(newUseArg);
parentInsn.replaceArg(useArg, newUseArg);
}
}
block.getInstructions().remove(0);
PhiInsn phiInsn = SSATransform.addPhi(mth, block, newRegNum);
phiInsn.setResult(insn.getResult());
phiInsn.bindArg(newAssignArg.duplicate(), assignPred);
phiInsn.bindArg(newArg.duplicate(),
BlockUtils.selectOtherSafe(assignPred, block.getPredecessors()));
}
private void initCodeVars(MethodNode mth) {
private static void initCodeVars(MethodNode mth) {
for (RegisterArg mthArg : mth.getArguments(true)) {
initCodeVar(mthArg.getSVar());
}
......@@ -144,7 +36,7 @@ public class EliminatePhiNodes extends AbstractVisitor {
}
}
private void initCodeVar(SSAVar ssaVar) {
public static void initCodeVar(SSAVar ssaVar) {
if (ssaVar.isCodeVarSet()) {
return;
}
......
package jadx.core.dex.visitors;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.Jadx;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.SplitterBlockAttr;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.dex.visitors.blocksmaker.helpers.FinallyExtractInfo;
import jadx.core.dex.visitors.blocksmaker.helpers.InsnsSlice;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.utils.BlockUtils;
@JadxVisitor(
name = "MarkFinallyVisitor",
desc = "Search and mark duplicate code generated for finally block",
runAfter = SSATransform.class,
runBefore = ConstInlineVisitor.class
)
public class MarkFinallyVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(MarkFinallyVisitor.class);
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
return;
}
try {
for (ExceptionHandler excHandler : mth.getExceptionHandlers()) {
processExceptionHandler(mth, excHandler);
}
} catch (Exception e) {
LOG.warn("Undo finally extract visitor, mth: {}", mth, e);
try {
// reload method without applying this visitor
// TODO: make more common and less hacky
mth.unload();
mth.load();
List<IDexTreeVisitor> passes = Jadx.getPassesList(mth.root().getArgs());
for (IDexTreeVisitor visitor : passes) {
if (visitor instanceof MarkFinallyVisitor) {
break;
}
visitor.init(mth.root());
DepthTraversal.visit(visitor, mth);
}
} catch (Exception ee) {
LOG.error("Undo finally extract failed, mth: {}", mth, e);
}
}
}
private static boolean processExceptionHandler(MethodNode mth, ExceptionHandler excHandler) {
// check if handler has exit edge to block not from this handler
boolean noExitNode = true;
InsnNode reThrowInsn = null;
for (BlockNode excBlock : excHandler.getBlocks()) {
if (noExitNode) {
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
}
List<InsnNode> insns = excBlock.getInstructions();
int size = insns.size();
if (excHandler.isCatchAll()
&& size != 0
&& insns.get(size - 1).getType() == InsnType.THROW) {
reThrowInsn = insns.get(size - 1);
}
}
if (noExitNode && reThrowInsn != null) {
boolean extracted = extractFinally(mth, excHandler);
if (extracted) {
reThrowInsn.add(AFlag.DONT_GENERATE);
}
return extracted;
}
return false;
}
/**
* Search and mark common code from 'try' block and 'handlers'.
*/
private static boolean extractFinally(MethodNode mth, ExceptionHandler allHandler) {
List<BlockNode> handlerBlocks = new ArrayList<>();
for (BlockNode block : allHandler.getBlocks()) {
InsnNode lastInsn = BlockUtils.getLastInsn(block);
if (lastInsn != null) {
InsnType insnType = lastInsn.getType();
if (insnType != InsnType.MOVE_EXCEPTION && insnType != InsnType.THROW) {
handlerBlocks.add(block);
}
}
}
if (handlerBlocks.isEmpty()) {
// remove empty catch
allHandler.getTryBlock().removeHandler(mth, allHandler);
return true;
}
BlockNode startBlock = handlerBlocks.get(0);
FinallyExtractInfo extractInfo = new FinallyExtractInfo(allHandler, startBlock, handlerBlocks);
// remove 'finally' from 'catch' handlers
TryCatchBlock tryBlock = allHandler.getTryBlock();
if (tryBlock.getHandlersCount() > 1) {
for (ExceptionHandler otherHandler : tryBlock.getHandlers()) {
if (otherHandler == allHandler) {
continue;
}
for (BlockNode checkBlock : otherHandler.getBlocks()) {
if (searchDuplicateInsns(checkBlock, extractInfo)) {
break;
}
}
}
if (extractInfo.getDuplicateSlices().size() != tryBlock.getHandlersCount() - 1) {
return false;
}
}
Set<BlockNode> splitters = new HashSet<>();
for (ExceptionHandler otherHandler : tryBlock.getHandlers()) {
SplitterBlockAttr splitterAttr = otherHandler.getHandlerBlock().get(AType.SPLITTER_BLOCK);
if (splitterAttr != null) {
BlockNode splBlock = splitterAttr.getBlock();
if (!splBlock.getCleanSuccessors().isEmpty()) {
splitters.add(splBlock);
}
}
}
// remove 'finally' from 'try' blocks (dominated by splitter block)
boolean found = false;
for (BlockNode splitter : splitters) {
BlockNode start = splitter.getCleanSuccessors().get(0);
List<BlockNode> list = BlockUtils.collectBlocksDominatedBy(splitter, start);
for (BlockNode block : list) {
if (searchDuplicateInsns(block, extractInfo)) {
found = true;
break;
}
}
}
if (!found) {
return false;
}
if (!checkSlices(extractInfo)) {
mth.addComment("JADX INFO: finally extract failed");
return false;
}
// 'finally' extract confirmed, apply
apply(extractInfo);
allHandler.setFinally(true);
return true;
}
private static boolean checkSlices(FinallyExtractInfo extractInfo) {
InsnsSlice finallySlice = extractInfo.getFinallyInsnsSlice();
List<InsnNode> finallyInsnsList = finallySlice.getInsnsList();
for (InsnsSlice dupSlice : extractInfo.getDuplicateSlices()) {
List<InsnNode> dupInsnsList = dupSlice.getInsnsList();
if (dupInsnsList.size() != finallyInsnsList.size()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Incorrect finally slice size: {}, expected: {}", dupSlice, finallySlice);
}
return false;
}
}
for (int i = 0; i < finallyInsnsList.size(); i++) {
InsnNode finallyInsn = finallyInsnsList.get(i);
for (InsnsSlice dupSlice : extractInfo.getDuplicateSlices()) {
List<InsnNode> insnsList = dupSlice.getInsnsList();
InsnNode dupInsn = insnsList.get(i);
if (finallyInsn.getType() != dupInsn.getType()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Incorrect finally slice insn: {}, expected: {}", dupInsn, finallyInsn);
}
return false;
}
}
}
return true;
}
private static void apply(FinallyExtractInfo extractInfo) {
markSlice(extractInfo.getFinallyInsnsSlice(), AFlag.FINALLY_INSNS);
for (InsnsSlice dupSlice : extractInfo.getDuplicateSlices()) {
markSlice(dupSlice, AFlag.DONT_GENERATE);
}
InsnsSlice finallySlice = extractInfo.getFinallyInsnsSlice();
List<InsnNode> finallyInsnsList = finallySlice.getInsnsList();
for (int i = 0; i < finallyInsnsList.size(); i++) {
InsnNode finallyInsn = finallyInsnsList.get(i);
for (InsnsSlice dupSlice : extractInfo.getDuplicateSlices()) {
InsnNode dupInsn = dupSlice.getInsnsList().get(i);
copyCodeVars(finallyInsn, dupInsn);
}
}
}
private static void markSlice(InsnsSlice slice, AFlag flag) {
List<InsnNode> insnsList = slice.getInsnsList();
for (InsnNode insn : insnsList) {
insn.add(flag);
}
for (BlockNode block : slice.getBlocks()) {
boolean allInsnMarked = true;
for (InsnNode insn : block.getInstructions()) {
if (!insn.contains(flag)) {
allInsnMarked = false;
break;
}
}
if (allInsnMarked) {
block.add(flag);
}
}
}
private static void copyCodeVars(InsnNode fromInsn, InsnNode toInsn) {
copyCodeVars(fromInsn.getResult(), toInsn.getResult());
int argsCount = fromInsn.getArgsCount();
for (int i = 0; i < argsCount; i++) {
copyCodeVars(fromInsn.getArg(i), toInsn.getArg(i));
}
}
private static void copyCodeVars(InsnArg fromArg, InsnArg toArg) {
if (fromArg == null || toArg == null
|| !fromArg.isRegister() || !toArg.isRegister()) {
return;
}
SSAVar fromSsaVar = ((RegisterArg) fromArg).getSVar();
SSAVar toSsaVar = ((RegisterArg) toArg).getSVar();
toSsaVar.setCodeVar(fromSsaVar.getCodeVar());
}
private static boolean searchDuplicateInsns(BlockNode checkBlock, FinallyExtractInfo extractInfo) {
boolean isNew = extractInfo.getCheckedBlocks().add(checkBlock);
if (!isNew) {
return false;
}
BlockNode startBlock = extractInfo.getStartBlock();
InsnsSlice dupSlice = searchFromFirstBlock(checkBlock, startBlock, extractInfo);
if (dupSlice == null) {
return false;
}
extractInfo.getDuplicateSlices().add(dupSlice);
return true;
}
private static InsnsSlice searchFromFirstBlock(BlockNode dupBlock, BlockNode startBlock, FinallyExtractInfo extractInfo) {
InsnsSlice dupSlice = isStartBlock(dupBlock, startBlock, extractInfo);
if (dupSlice == null) {
return null;
}
if (dupSlice.isComplete()) {
return dupSlice;
}
if (!checkBlocksTree(dupBlock, startBlock, dupSlice, extractInfo)) {
return null;
}
return dupSlice;
}
/**
* 'Finally' instructions can start in the middle of the first block.
*/
private static InsnsSlice isStartBlock(BlockNode dupBlock, BlockNode finallyBlock, FinallyExtractInfo extractInfo) {
List<InsnNode> dupInsns = dupBlock.getInstructions();
List<InsnNode> finallyInsns = finallyBlock.getInstructions();
if (dupInsns.size() < finallyInsns.size()) {
return null;
}
int startPos = dupInsns.size() - finallyInsns.size();
int endPos = 0;
// fast check from end of block
if (!checkInsns(dupInsns, finallyInsns, startPos)) {
// check from block start
if (checkInsns(dupInsns, finallyInsns, 0)) {
startPos = 0;
endPos = finallyInsns.size();
} else {
// search start insn
boolean found = false;
for (int i = 1; i < startPos; i++) {
if (checkInsns(dupInsns, finallyInsns, i)) {
startPos = i;
endPos = finallyInsns.size() + i;
found = true;
break;
}
}
if (!found) {
return null;
}
}
}
// put instructions into slices
boolean complete;
InsnsSlice slice = new InsnsSlice();
int endIndex;
if (endPos != 0) {
endIndex = endPos + 1;
// both slices completed
complete = true;
} else {
endIndex = dupInsns.size();
complete = false;
}
// fill dup insns slice
for (int i = startPos; i < endIndex; i++) {
slice.addInsn(dupInsns.get(i), dupBlock);
}
// fill finally insns slice
InsnsSlice finallySlice = extractInfo.getFinallyInsnsSlice();
if (finallySlice.isComplete()) {
// compare slices
if (finallySlice.getInsnsList().size() != slice.getInsnsList().size()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Another duplicated slice has different insns count: {}, finally: {}", slice, finallySlice);
}
return null;
}
// TODO: add additional slices checks
// and try to extract common part if found difference
} else {
for (InsnNode finallyInsn : finallyInsns) {
finallySlice.addInsn(finallyInsn, finallyBlock);
}
}
if (complete) {
slice.setComplete(true);
finallySlice.setComplete(true);
}
return slice;
}
private static boolean checkInsns(List<InsnNode> remInsns, List<InsnNode> finallyInsns, int delta) {
for (int i = finallyInsns.size() - 1; i >= 0; i--) {
InsnNode startInsn = finallyInsns.get(i);
InsnNode remInsn = remInsns.get(delta + i);
if (!sameInsns(remInsn, startInsn)) {
return false;
}
}
return true;
}
private static boolean checkBlocksTree(BlockNode dupBlock, BlockNode finallyBlock,
InsnsSlice dupSlice, FinallyExtractInfo extractInfo) {
InsnsSlice finallySlice = extractInfo.getFinallyInsnsSlice();
List<BlockNode> finallyCS = finallyBlock.getCleanSuccessors();
List<BlockNode> dupCS = dupBlock.getCleanSuccessors();
if (finallyCS.size() == dupCS.size()) {
for (int i = 0; i < finallyCS.size(); i++) {
BlockNode finSBlock = finallyCS.get(i);
BlockNode dupSBlock = dupCS.get(i);
if (extractInfo.getAllHandlerBlocks().contains(finSBlock)) {
if (!compareBlocks(dupSBlock, finSBlock, dupSlice, extractInfo)) {
return false;
}
if (!checkBlocksTree(dupSBlock, finSBlock, dupSlice, extractInfo)) {
return false;
}
dupSlice.addBlock(dupSBlock);
finallySlice.addBlock(finSBlock);
}
}
}
dupSlice.setComplete(true);
finallySlice.setComplete(true);
return true;
}
private static boolean compareBlocks(BlockNode dupBlock, BlockNode finallyBlock, InsnsSlice dupSlice, FinallyExtractInfo extractInfo) {
List<InsnNode> dupInsns = dupBlock.getInstructions();
List<InsnNode> finallyInsns = finallyBlock.getInstructions();
if (dupInsns.size() < finallyInsns.size()) {
return false;
}
int size = finallyInsns.size();
for (int i = 0; i < size; i++) {
if (!sameInsns(dupInsns.get(i), finallyInsns.get(i))) {
return false;
}
}
if (dupInsns.size() > finallyInsns.size()) {
dupSlice.addInsns(dupBlock, 0, finallyInsns.size());
dupSlice.setComplete(true);
InsnsSlice finallyInsnsSlice = extractInfo.getFinallyInsnsSlice();
finallyInsnsSlice.addBlock(finallyBlock);
finallyInsnsSlice.setComplete(true);
}
return true;
}
private static boolean sameInsns(InsnNode remInsn, InsnNode fInsn) {
if (!remInsn.isSame(fInsn)) {
return false;
}
// TODO: check instance arg in ConstructorInsn
// TODO: compare literals
for (int i = 0; i < remInsn.getArgsCount(); i++) {
InsnArg remArg = remInsn.getArg(i);
InsnArg fArg = fInsn.getArg(i);
if (remArg.isRegister() != fArg.isRegister()) {
return false;
}
}
return true;
}
}
......@@ -235,7 +235,6 @@ public class ModVisitor extends AbstractVisitor {
RegisterArg reg = (RegisterArg) arg;
SSAVar sVar = reg.getSVar();
if (sVar != null) {
sVar.add(AFlag.FINAL);
sVar.getCodeVar().setFinal(true);
sVar.add(AFlag.DONT_INLINE);
}
......
......@@ -36,6 +36,9 @@ public class PrepareForCodeGen extends AbstractVisitor {
return;
}
for (BlockNode block : blocks) {
if (block.contains(AFlag.DONT_GENERATE)) {
continue;
}
removeInstructions(block);
checkInline(block);
// removeParenthesis(block);
......
......@@ -4,13 +4,21 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jadx.core.dex.instructions.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.Consts;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.ArithNode;
import jadx.core.dex.instructions.ArithOp;
import jadx.core.dex.instructions.CallMthInterface;
import jadx.core.dex.instructions.ConstStringNode;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.FieldArg;
import jadx.core.dex.instructions.args.InsnArg;
......@@ -44,6 +52,9 @@ public class SimplifyVisitor extends AbstractVisitor {
}
private static InsnNode simplifyInsn(MethodNode mth, InsnNode insn) {
if (insn.contains(AFlag.DONT_GENERATE)) {
return null;
}
for (InsnArg arg : insn.getArguments()) {
if (arg.isInsnWrap()) {
InsnNode ni = simplifyInsn(mth, ((InsnWrapArg) arg).getWrapInsn());
......
......@@ -49,19 +49,17 @@ public class BlockExceptionHandler extends AbstractVisitor {
}
ExceptionHandler excHandler = handlerAttr.getHandler();
ArgType argType = excHandler.getArgType();
if (!block.getInstructions().isEmpty()) {
InsnNode me = block.getInstructions().get(0);
if (me.getType() == InsnType.MOVE_EXCEPTION) {
// set correct type for 'move-exception' operation
RegisterArg resArg = InsnArg.reg(me.getResult().getRegNum(), argType);
resArg.copyAttributesFrom(me);
me.setResult(resArg);
me.add(AFlag.DONT_INLINE);
resArg.add(AFlag.CUSTOM_DECLARE);
excHandler.setArg(resArg);
me.addAttr(handlerAttr);
return;
}
InsnNode me = BlockUtils.getLastInsn(block);
if (me != null && me.getType() == InsnType.MOVE_EXCEPTION) {
// set correct type for 'move-exception' operation
RegisterArg resArg = InsnArg.reg(me.getResult().getRegNum(), argType);
resArg.copyAttributesFrom(me);
me.setResult(resArg);
me.add(AFlag.DONT_INLINE);
resArg.add(AFlag.CUSTOM_DECLARE);
excHandler.setArg(resArg);
me.addAttr(handlerAttr);
return;
}
// handler arguments not used
excHandler.setArg(new NamedArg("unused", argType));
......
package jadx.core.dex.visitors.blocksmaker;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.Jadx;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
import jadx.core.dex.instructions.InsnType;
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.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.CatchAttr;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.trycatch.SplitterBlockAttr;
import jadx.core.dex.trycatch.TryCatchBlock;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor;
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.ErrorsCounter;
import jadx.core.utils.exceptions.JadxRuntimeException;
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 {
private static final Logger LOG = LoggerFactory.getLogger(BlockFinallyExtract.class);
@Override
public void visit(MethodNode mth) {
if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
return;
}
try {
boolean reloadBlocks = false;
for (ExceptionHandler excHandler : mth.getExceptionHandlers()) {
if (processExceptionHandler(mth, excHandler)) {
reloadBlocks = true;
}
}
if (reloadBlocks) {
mergeReturnBlocks(mth);
BlockProcessor.rerun(mth);
}
} catch (Exception e) {
LOG.warn("Undo finally extract visitor, mth: {}", mth, e);
try {
// reload method without applying this visitor
// TODO: make more common and less hacky
mth.unload();
mth.load();
List<IDexTreeVisitor> passes = Jadx.getPassesList(mth.root().getArgs());
for (IDexTreeVisitor visitor : passes) {
if (visitor instanceof BlockFinallyExtract) {
break;
}
DepthTraversal.visit(visitor, mth);
}
} catch (Exception ee) {
LOG.error("Undo finally extract failed, mth: {}", mth, e);
}
}
}
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;
for (BlockNode excBlock : excHandler.getBlocks()) {
if (noExitNode) {
noExitNode = excHandler.getBlocks().containsAll(excBlock.getCleanSuccessors());
}
List<InsnNode> insns = excBlock.getInstructions();
int size = insns.size();
if (excHandler.isCatchAll()
&& size != 0
&& insns.get(size - 1).getType() == InsnType.THROW) {
reThrowRemoved = true;
insns.remove(size - 1);
}
}
if (reThrowRemoved && noExitNode
&& extractFinally(mth, excHandler)) {
return true;
}
int totalSize = countInstructions(excHandler);
if (totalSize == 0 && reThrowRemoved && noExitNode) {
excHandler.getTryBlock().removeHandler(mth, excHandler);
}
return false;
}
/**
* Search and remove common code from 'catch' and 'handlers'.
*/
private static boolean extractFinally(MethodNode mth, ExceptionHandler handler) {
int count = handler.getBlocks().size();
BitSet bs = new BitSet(count);
List<BlockNode> blocks = new ArrayList<>(count);
for (BlockNode block : handler.getBlocks()) {
List<InsnNode> insns = block.getInstructions();
if (!insns.isEmpty()) {
if (insns.get(0).getType() != InsnType.MOVE_EXCEPTION) {
blocks.add(block);
}
bs.set(block.getId());
}
}
if (blocks.isEmpty()) {
// nothing to do
return false;
}
List<BlocksRemoveInfo> removes = new LinkedList<>();
Set<BlockNode> splitters = new HashSet<>();
// remove 'finally' from handlers
TryCatchBlock tryBlock = handler.getTryBlock();
if (tryBlock.getHandlersCount() > 1) {
for (ExceptionHandler otherHandler : tryBlock.getHandlers()) {
if (otherHandler == handler) {
continue;
}
for (BlockNode hb : otherHandler.getBlocks()) {
BlocksRemoveInfo removeInfo = removeInsns(mth, hb, blocks, bs);
if (removeInfo != null) {
removes.add(removeInfo);
break;
}
}
}
if (removes.size() != tryBlock.getHandlersCount() - 1) {
return false;
}
}
for (ExceptionHandler otherHandler : tryBlock.getHandlers()) {
SplitterBlockAttr splitterAttr = otherHandler.getHandlerBlock().get(AType.SPLITTER_BLOCK);
if (splitterAttr != null) {
BlockNode splBlock = splitterAttr.getBlock();
if (!splBlock.getCleanSuccessors().isEmpty()) {
splitters.add(splBlock);
}
}
}
// remove 'finally' from 'try' blocks (dominated by splitter block)
boolean removed = false;
for (BlockNode splitter : splitters) {
BlockNode start = splitter.getCleanSuccessors().get(0);
List<BlockNode> list = BlockUtils.collectBlocksDominatedBy(splitter, start);
for (BlockNode block : list) {
if (bs.get(block.getId())) {
continue;
}
BlocksRemoveInfo removeInfo = removeInsns(mth, block, blocks, bs);
if (removeInfo != null) {
removes.add(removeInfo);
removed = true;
break;
}
}
}
if (!removed) {
return false;
}
// 'finally' extract confirmed, run remove steps
LiveVarAnalysis laBefore = null;
boolean runReMap = isReMapNeeded(removes);
if (runReMap) {
laBefore = new LiveVarAnalysis(mth);
laBefore.runAnalysis();
}
int removeApplied = 0;
for (BlocksRemoveInfo removeInfo : removes) {
if (applyRemove(mth, removeInfo)) {
removeApplied++;
removeInfo.setApplied(true);
}
}
if (removeApplied == 0) {
return false;
}
if (removeApplied != removes.size()) {
throw new JadxRuntimeException("Some finally instructions failed to remove: "
+ removes.stream().filter(n -> !n.isApplied()).map(BlocksRemoveInfo::toString).collect(Collectors.joining(","))
);
}
LiveVarAnalysis laAfter = null;
// remove 'move-exception' instruction
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, 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.REMOVE);
}
}
// 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();
BlocksPair start = removeInfo.getStart();
BlockNode insertBlockBefore = start.getFirst();
BlockNode insertBlock = start.getSecond();
if (removeInfo.getRegMap().isEmpty() || insertBlock == null) {
continue;
}
for (Map.Entry<RegisterArg, RegisterArg> entry : removeInfo.getRegMap().entrySet()) {
RegisterArg fromReg = entry.getKey();
RegisterArg toReg = entry.getValue();
int fromRegNum = fromReg.getRegNum();
int toRegNum = toReg.getRegNum();
if (!processed.get(fromRegNum)) {
boolean liveFromBefore = laBefore.isLive(insertBlockBefore, fromRegNum);
boolean liveFromAfter = laAfter.isLive(insertBlock, fromRegNum);
boolean liveToAfter = laAfter.isLive(insertBlock, toRegNum);
if (liveToAfter && liveFromBefore) {
// merge 'to' and 'from' registers
InsnNode merge = new InsnNode(InsnType.MERGE, 2);
merge.setResult(toReg.duplicate());
merge.addArg(toReg.duplicate());
merge.addArg(fromReg.duplicate());
injectInsn(mth, insertBlock, merge);
} else if (liveFromBefore) {
// remap variable
InsnNode move = new InsnNode(InsnType.MOVE, 1);
move.setResult(toReg.duplicate());
move.addArg(fromReg.duplicate());
injectInsn(mth, insertBlock, move);
} else if (liveFromAfter) {
// kill variable
InsnNode kill = new InsnNode(InsnType.NOP, 0);
kill.setResult(fromReg.duplicate());
kill.add(AFlag.REMOVE);
injectInsn(mth, insertBlock, kill);
}
processed.set(fromRegNum);
}
}
}
}
private static void injectInsn(MethodNode mth, BlockNode insertBlock, InsnNode insn) {
insn.add(AFlag.SYNTHETIC);
if (insertBlock.getInstructions().isEmpty()) {
insertBlock.getInstructions().add(insn);
} else {
BlockNode succBlock = splitBlock(mth, insertBlock, 0);
BlockNode predBlock = succBlock.getPredecessors().get(0);
predBlock.getInstructions().add(insn);
}
}
private static boolean isReMapNeeded(List<BlocksRemoveInfo> removes) {
for (BlocksRemoveInfo removeInfo : removes) {
if (!removeInfo.getRegMap().isEmpty()) {
return true;
}
}
return false;
}
private static BlocksRemoveInfo removeInsns(MethodNode mth, BlockNode remBlock, List<BlockNode> blocks, BitSet bs) {
if (blocks.isEmpty()) {
return null;
}
BlockNode startBlock = blocks.get(0);
BlocksRemoveInfo removeInfo = checkFromFirstBlock(remBlock, startBlock, bs);
if (removeInfo == null) {
return null;
}
Set<BlocksPair> outs = removeInfo.getOuts();
if (outs.size() == 1) {
return removeInfo;
}
// check if several 'return' blocks maps to one out
if (mergeReturns(mth, outs)) {
return removeInfo;
}
LOG.debug("Unexpected finally block outs count: {}", outs);
return null;
}
private static boolean mergeReturns(MethodNode mth, Set<BlocksPair> outs) {
Set<BlockNode> rightOuts = new HashSet<>();
boolean allReturns = true;
for (BlocksPair outPair : outs) {
BlockNode first = outPair.getFirst();
if (!first.isReturnBlock()) {
allReturns = false;
}
rightOuts.add(outPair.getSecond());
}
if (!allReturns || rightOuts.size() != 1) {
return false;
}
Iterator<BlocksPair> it = outs.iterator();
while (it.hasNext()) {
BlocksPair out = it.next();
BlockNode returnBlock = out.getFirst();
if (!returnBlock.contains(AFlag.ORIG_RETURN)) {
markForRemove(mth, returnBlock);
it.remove();
}
}
return true;
}
private static BlocksRemoveInfo checkFromFirstBlock(BlockNode remBlock, BlockNode startBlock, BitSet bs) {
BlocksRemoveInfo removeInfo = isStartBlock(remBlock, startBlock);
if (removeInfo == null) {
return null;
}
if (!checkBlocksTree(remBlock, startBlock, removeInfo, bs)) {
return null;
}
return removeInfo;
}
/**
* 'Finally' instructions can start in the middle of the first block.
*/
private static @Nullable
BlocksRemoveInfo isStartBlock(BlockNode remBlock, BlockNode startBlock) {
List<InsnNode> remInsns = remBlock.getInstructions();
List<InsnNode> startInsns = startBlock.getInstructions();
if (remInsns.size() < startInsns.size()) {
return null;
}
// first - fast check
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);
}
// second - run checks again for collect registers mapping
if (!checkInsns(remInsns, startInsns, startPos, removeInfo)) {
return null;
}
return removeInfo;
}
private static boolean checkInsns(List<InsnNode> remInsns, List<InsnNode> startInsns, int delta,
@Nullable BlocksRemoveInfo removeInfo) {
for (int i = startInsns.size() - 1; i >= 0; i--) {
InsnNode startInsn = startInsns.get(i);
InsnNode remInsn = remInsns.get(delta + i);
if (!sameInsns(remInsn, startInsn, removeInfo)) {
return false;
}
}
return true;
}
private static boolean checkBlocksTree(BlockNode remBlock, BlockNode startBlock,
@NotNull BlocksRemoveInfo removeInfo, BitSet bs) {
// skip check on start block
if (!removeInfo.getProcessed().isEmpty()
&& !sameBlocks(remBlock, startBlock, removeInfo)) {
return false;
}
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(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;
}
} else {
removeInfo.getOuts().add(new BlocksPair(rBlock, sBlock));
}
}
return true;
}
private static boolean sameBlocks(BlockNode remBlock, BlockNode finallyBlock,
@NotNull BlocksRemoveInfo removeInfo) {
List<InsnNode> first = remBlock.getInstructions();
List<InsnNode> second = finallyBlock.getInstructions();
if (first.size() < second.size()) {
return false;
}
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;
}
private static boolean sameInsns(InsnNode remInsn, InsnNode fInsn, @Nullable BlocksRemoveInfo removeInfo) {
if (!remInsn.isSame(fInsn)) {
return false;
}
// TODO: check instance arg in ConstructorInsn
// TODO: compare literals
for (int i = 0; i < remInsn.getArgsCount(); i++) {
InsnArg remArg = remInsn.getArg(i);
InsnArg fArg = fInsn.getArg(i);
if (remArg.isRegister() != fArg.isRegister()) {
return false;
}
if (removeInfo != null && fArg.isRegister()) {
RegisterArg remReg = (RegisterArg) remArg;
RegisterArg fReg = (RegisterArg) fArg;
if (remReg.getRegNum() != fReg.getRegNum()) {
RegisterArg mapReg = removeInfo.getRegMap().get(remArg);
if (mapReg == null) {
removeInfo.getRegMap().put(remReg, fReg);
} else if (!mapReg.equalRegister(fReg)) {
return false;
}
}
}
}
return true;
}
private static boolean applyRemove(MethodNode mth, BlocksRemoveInfo removeInfo) {
BlockNode remBlock = removeInfo.getStart().getFirst();
BlockNode startBlock = removeInfo.getStart().getSecond();
if (remBlock.contains(AFlag.REMOVE)) {
// already processed
return true;
}
if (remBlock.getPredecessors().size() != 1) {
LOG.warn("Finally extract failed: remBlock pred: {}, {}, method: {}", remBlock, remBlock.getPredecessors(), mth);
return false;
}
if (removeInfo.getOuts().isEmpty()) {
ErrorsCounter.methodWarn(mth, "Failed to extract finally block: empty outs");
return false;
}
// safe checks finished, altering blocks tree
// all error must throw exception to undo changes
BlockNode remBlockPred = remBlock.getPredecessors().get(0);
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);
}
}
}
Set<BlocksPair> outs = removeInfo.getOuts();
if (outs.isEmpty()) {
throw new JadxRuntimeException("Failed to extract finally block: all outs is deleted");
}
BlocksPair out = outs.iterator().next();
BlockNode rOut = out.getFirst();
BlockNode sOut = out.getSecond();
// redirect out edges
List<BlockNode> filtPreds = BlockUtils.filterPredecessors(sOut);
if (filtPreds.size() > 1) {
BlockNode pred = sOut.getPredecessors().get(0);
BlockNode newPred = BlockSplitter.insertBlockBetween(mth, pred, sOut);
for (BlockNode predBlock : new ArrayList<>(sOut.getPredecessors())) {
if (predBlock != newPred) {
BlockSplitter.replaceConnection(predBlock, sOut, newPred);
}
}
rOut.getPredecessors().clear();
addIgnoredEdge(newPred, rOut);
connect(newPred, rOut);
} else if (filtPreds.size() == 1) {
BlockNode pred = filtPreds.get(0);
BlockNode repl = removeInfo.getBySecond(pred);
if (repl == null) {
throw new JadxRuntimeException("Block not found by " + pred + ", in " + removeInfo);
}
removeConnection(pred, rOut);
addIgnoredEdge(repl, rOut);
connect(repl, rOut);
} else {
throw new JadxRuntimeException("Finally extract failed, unexpected preds: " + filtPreds
+ " for " + sOut + ", method: " + mth);
}
// redirect input edges
for (BlockNode pred : new ArrayList<>(remBlock.getPredecessors())) {
BlockNode middle = insertBlockBetween(mth, pred, remBlock);
removeConnection(middle, remBlock);
connect(middle, startBlock);
addIgnoredEdge(middle, startBlock);
connect(middle, rOut);
BlockSplitter.replaceTarget(middle, remBlock, rOut);
}
// mark blocks for remove
markForRemove(mth, remBlock);
for (BlocksPair pair : removeInfo.getProcessed()) {
markForRemove(mth, pair.getFirst());
BlockNode second = pair.getSecond();
second.updateCleanSuccessors();
}
return true;
}
/**
* Split one block into connected 2 blocks with same connections.
*
* @return new successor block
*/
private static BlockNode splitBlock(MethodNode mth, BlockNode block, int splitIndex) {
BlockNode newBlock = BlockSplitter.startNewBlock(mth, -1);
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<InsnNode> insns = block.getInstructions();
int size = insns.size();
for (int i = splitIndex; i < size; i++) {
InsnNode insnNode = insns.get(i);
insnNode.add(AFlag.DONT_GENERATE);
newBlock.getInstructions().add(insnNode);
}
Iterator<InsnNode> it = insns.iterator();
while (it.hasNext()) {
InsnNode insnNode = it.next();
if (insnNode.contains(AFlag.DONT_GENERATE)) {
it.remove();
}
}
for (InsnNode insnNode : newBlock.getInstructions()) {
insnNode.remove(AFlag.DONT_GENERATE);
}
return newBlock;
}
/**
* Unbind block for removing.
*/
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);
}
block.getPredecessors().clear();
block.getSuccessors().clear();
block.add(AFlag.REMOVE);
block.remove(AFlag.DONT_GENERATE);
CatchAttr catchAttr = block.get(AType.CATCH_BLOCK);
if (catchAttr != null) {
catchAttr.getTryBlock().removeBlock(mth, block);
for (BlockNode skipBlock : mth.getBasicBlocks()) {
if (skipBlock.contains(AFlag.REMOVE)) {
markForRemove(mth, skipBlock);
}
}
}
}
private static void addIgnoredEdge(BlockNode from, BlockNode toBlock) {
IgnoreEdgeAttr edgeAttr = from.get(AType.IGNORE_EDGE);
if (edgeAttr == null) {
edgeAttr = new IgnoreEdgeAttr();
from.addAttr(edgeAttr);
}
edgeAttr.getBlocks().add(toBlock);
}
private static int countInstructions(ExceptionHandler excHandler) {
int totalSize = 0;
for (BlockNode excBlock : excHandler.getBlocks()) {
List<InsnNode> list = excBlock.getInstructions();
if (!list.isEmpty() && list.get(0).getType() == InsnType.MOVE_EXCEPTION) {
// don't count 'move-exception' it will be removed later
totalSize--;
}
totalSize += list.size();
}
return totalSize;
}
/**
* Merge return block with same predecessor.
*/
private static void mergeReturnBlocks(MethodNode mth) {
List<BlockNode> exitBlocks = mth.getExitBlocks();
BlockNode pred = getFinallyOutBlock(exitBlocks);
if (pred == null) {
return;
}
IgnoreEdgeAttr edgeAttr = pred.get(AType.IGNORE_EDGE);
if (edgeAttr == null) {
return;
}
List<BlockNode> merge = new LinkedList<>();
for (BlockNode blockNode : pred.getSuccessors()) {
if (blockNode.contains(AFlag.RETURN)) {
merge.add(blockNode);
}
}
if (merge.size() < 2) {
return;
}
// select 'original' return block
BlockNode origReturnBlock = null;
for (BlockNode ret : merge) {
if (ret.contains(AFlag.ORIG_RETURN)) {
origReturnBlock = ret;
break;
}
}
if (origReturnBlock == null) {
return;
}
for (BlockNode mb : merge) {
if (mb == origReturnBlock) {
continue;
}
for (BlockNode remPred : mb.getPredecessors()) {
connect(remPred, origReturnBlock);
}
markForRemove(mth, mb);
edgeAttr.getBlocks().remove(mb);
}
mergeSyntheticPredecessors(mth, origReturnBlock);
}
private static void mergeSyntheticPredecessors(MethodNode mth, BlockNode block) {
List<BlockNode> preds = new ArrayList<>(block.getPredecessors());
Iterator<BlockNode> it = preds.iterator();
while (it.hasNext()) {
BlockNode predBlock = it.next();
if (!predBlock.isSynthetic()) {
it.remove();
}
}
if (preds.size() < 2) {
return;
}
BlockNode commonBlock = null;
for (BlockNode predBlock : preds) {
List<BlockNode> successors = predBlock.getSuccessors();
if (successors.size() != 2) {
return;
}
BlockNode cmnBlk = BlockUtils.selectOtherSafe(block, successors);
if (commonBlock == null) {
commonBlock = cmnBlk;
} else if (cmnBlk != commonBlock) {
return;
}
if (!predBlock.contains(AType.IGNORE_EDGE)) {
return;
}
}
if (commonBlock == null) {
return;
}
// merge confirmed
BlockNode mergeBlock = null;
for (BlockNode predBlock : preds) {
if (mergeBlock == null) {
mergeBlock = predBlock;
continue;
}
for (BlockNode remPred : predBlock.getPredecessors()) {
connect(remPred, mergeBlock);
}
markForRemove(mth, predBlock);
}
}
private static BlockNode getFinallyOutBlock(List<BlockNode> exitBlocks) {
for (BlockNode exitBlock : exitBlocks) {
for (BlockNode exitPred : exitBlock.getPredecessors()) {
IgnoreEdgeAttr edgeAttr = exitPred.get(AType.IGNORE_EDGE);
if (edgeAttr != null && edgeAttr.contains(exitBlock)) {
return exitPred;
}
}
}
return null;
}
}
......@@ -560,11 +560,8 @@ public class BlockProcessor extends AbstractVisitor {
}
private static RegisterArg getMoveExceptionRegister(BlockNode block) {
if (block.getInstructions().isEmpty()) {
return null;
}
InsnNode insn = block.getInstructions().get(0);
if (insn.getType() != InsnType.MOVE_EXCEPTION) {
InsnNode insn = BlockUtils.getLastInsn(block);
if (insn == null || insn.getType() != InsnType.MOVE_EXCEPTION) {
return null;
}
return insn.getResult();
......
package jadx.core.dex.visitors.blocksmaker.helpers;
import jadx.core.dex.nodes.BlockNode;
public final class BlocksPair {
private final BlockNode first;
private final BlockNode second;
public BlocksPair(BlockNode first, BlockNode second) {
this.first = first;
this.second = second;
}
public BlockNode getFirst() {
return first;
}
public BlockNode getSecond() {
return second;
}
@Override
public int hashCode() {
return 31 * first.hashCode() + second.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BlocksPair)) {
return false;
}
BlocksPair other = (BlocksPair) o;
return first.equals(other.first) && second.equals(other.second);
}
@Override
public String toString() {
return "(" + first + ", " + second + ")";
}
}
package jadx.core.dex.visitors.blocksmaker.helpers;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.BlockNode;
public final class BlocksRemoveInfo {
private final Set<BlocksPair> processed = new HashSet<>();
private final Set<BlocksPair> outs = new HashSet<>();
private final Map<RegisterArg, RegisterArg> regMap = new HashMap<>();
private BlocksPair start;
private BlocksPair end;
private int startSplitIndex;
private int endSplitIndex;
private BlockNode startPredecessor;
private boolean applied;
public BlocksRemoveInfo(BlocksPair start) {
this.start = start;
}
public Set<BlocksPair> getProcessed() {
return processed;
}
public Set<BlocksPair> getOuts() {
return outs;
}
public BlocksPair getStart() {
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;
}
public void setStartSplitIndex(int startSplitIndex) {
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;
}
@Nullable
public BlockNode getByFirst(BlockNode first) {
for (BlocksPair blocksPair : processed) {
if (blocksPair.getFirst() == first) {
return blocksPair.getSecond();
}
}
return null;
}
@Nullable
public BlockNode getBySecond(BlockNode second) {
for (BlocksPair blocksPair : processed) {
if (blocksPair.getSecond() == second) {
return blocksPair.getSecond();
}
}
return null;
}
public boolean isApplied() {
return applied;
}
public void setApplied(boolean applied) {
this.applied = applied;
}
@Override
public String toString() {
return "BRI{start: " + start
+ ", end: " + end
+ ", processed: " + processed
+ ", outs: " + outs
+ ", regMap: " + regMap
+ ", split: " + startSplitIndex + "-" + endSplitIndex
+ "}";
}
}
package jadx.core.dex.visitors.blocksmaker.helpers;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.trycatch.ExceptionHandler;
public class FinallyExtractInfo {
private final ExceptionHandler finallyHandler;
private final List<BlockNode> allHandlerBlocks;
private final List<InsnsSlice> duplicateSlices = new ArrayList<>();
private final Set<BlockNode> checkedBlocks = new HashSet<>();
private final InsnsSlice finallyInsnsSlice = new InsnsSlice();
private final BlockNode startBlock;
public FinallyExtractInfo(ExceptionHandler finallyHandler, BlockNode startBlock, List<BlockNode> allHandlerBlocks) {
this.finallyHandler = finallyHandler;
this.startBlock = startBlock;
this.allHandlerBlocks = allHandlerBlocks;
}
public ExceptionHandler getFinallyHandler() {
return finallyHandler;
}
public List<BlockNode> getAllHandlerBlocks() {
return allHandlerBlocks;
}
public InsnsSlice getFinallyInsnsSlice() {
return finallyInsnsSlice;
}
public List<InsnsSlice> getDuplicateSlices() {
return duplicateSlices;
}
public Set<BlockNode> getCheckedBlocks() {
return checkedBlocks;
}
public BlockNode getStartBlock() {
return startBlock;
}
}
package jadx.core.dex.visitors.blocksmaker.helpers;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
public class InsnsSlice {
private final List<InsnNode> insnsList = new ArrayList<>();
private final Map<InsnNode, BlockNode> insnMap = new IdentityHashMap<>();
private boolean complete;
public void addInsn(InsnNode insn, BlockNode block) {
insnsList.add(insn);
insnMap.put(insn, block);
}
public void addBlock(BlockNode block) {
for (InsnNode insn : block.getInstructions()) {
addInsn(insn, block);
}
}
public void addInsns(BlockNode block, int startIndex, int endIndex) {
List<InsnNode> insns = block.getInstructions();
for (int i = startIndex; i < endIndex; i++) {
addInsn(insns.get(i), block);
}
}
@Nullable
public BlockNode getBlock(InsnNode insn) {
return insnMap.get(insn);
}
public List<InsnNode> getInsnsList() {
return insnsList;
}
public Set<BlockNode> getBlocks() {
Set<BlockNode> set = new LinkedHashSet<>();
for (InsnNode insn : insnsList) {
set.add(insnMap.get(insn));
}
return set;
}
public boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
@Override
public String toString() {
return "{["
+ insnsList.stream().map(insn -> insn.getType().toString()).collect(Collectors.joining(", "))
+ "]"
+ (complete ? " complete" : "")
+ "}";
}
}
......@@ -26,7 +26,6 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.ssa.EliminatePhiNodes;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor;
import jadx.core.dex.visitors.typeinference.TypeUpdateResult;
......@@ -39,8 +38,7 @@ import jadx.core.utils.exceptions.JadxException;
desc = "Apply debug info to registers (type and names)",
runAfter = {
SSATransform.class,
TypeInferenceVisitor.class,
EliminatePhiNodes.class
TypeInferenceVisitor.class
}
)
public class DebugInfoApplyVisitor extends AbstractVisitor {
......
......@@ -59,6 +59,7 @@ public class CheckRegions extends AbstractVisitor {
if (!blocksInRegions.contains(block)
&& !block.getInstructions().isEmpty()
&& !block.contains(AFlag.ADDED_TO_REGION)
&& !block.contains(AFlag.DONT_GENERATE)
&& !block.contains(AFlag.REMOVE)) {
String blockCode = getBlockInsnStr(mth, block);
mth.addWarn("Missing block: " + block + ", code skipped:" + CodeWriter.NL + blockCode);
......
package jadx.core.dex.visitors.regions;
import java.util.List;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.regions.Region;
import jadx.core.dex.visitors.AbstractVisitor;
public class CleanRegions extends AbstractVisitor {
private static final IRegionVisitor REMOVE_REGION_VISITOR = new RemoveRegionVisitor();
public class CleanRegions {
@Override
public void visit(MethodNode mth) {
process(mth);
}
public static void process(MethodNode mth) {
if (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {
return;
}
IRegionVisitor removeEmptyBlocks = new AbstractRegionVisitor() {
@Override
public boolean enterRegion(MethodNode mth, IRegion region) {
if (region instanceof Region) {
region.getSubBlocks().removeIf(container -> {
if (container instanceof BlockNode) {
BlockNode block = (BlockNode) container;
return block.getInstructions().isEmpty();
}
DepthRegionTraversal.traverse(mth, REMOVE_REGION_VISITOR);
}
private static class RemoveRegionVisitor extends AbstractRegionVisitor {
@Override
public boolean enterRegion(MethodNode mth, IRegion region) {
if (region instanceof Region) {
region.getSubBlocks().removeIf(RemoveRegionVisitor::canRemoveRegion);
}
return true;
}
private static boolean canRemoveRegion(IContainer container) {
if (container.contains(AFlag.DONT_GENERATE)) {
return true;
}
if (container instanceof BlockNode) {
BlockNode block = (BlockNode) container;
return block.getInstructions().isEmpty();
}
if (container instanceof IRegion) {
List<IContainer> subBlocks = ((IRegion) container).getSubBlocks();
for (IContainer subBlock : subBlocks) {
if (!canRemoveRegion(subBlock)) {
return false;
});
}
}
return true;
}
};
DepthRegionTraversal.traverse(mth, removeEmptyBlocks);
}
private CleanRegions() {
return false;
}
}
}
......@@ -36,7 +36,10 @@ public class IfMakerHelper {
}
static IfInfo makeIfInfo(BlockNode ifBlock) {
IfNode ifNode = (IfNode) ifBlock.getInstructions().get(0);
IfNode ifNode = (IfNode) BlockUtils.getLastInsn(ifBlock);
if (ifNode == null) {
throw new JadxRuntimeException("Empty IF block: " + ifBlock);
}
IfCondition condition = IfCondition.fromIfNode(ifNode);
IfInfo info = new IfInfo(condition, ifNode.getThenBlock(), ifNode.getElseBlock());
info.setIfBlock(ifBlock);
......@@ -328,8 +331,8 @@ public class IfMakerHelper {
if (block == null || block.contains(AType.LOOP) || block.contains(AFlag.ADDED_TO_REGION)) {
return null;
}
List<InsnNode> insns = block.getInstructions();
if (insns.size() == 1 && insns.get(0).getType() == InsnType.IF) {
InsnNode lastInsn = BlockUtils.getLastInsn(block);
if (lastInsn != null && lastInsn.getType() == InsnType.IF) {
return block;
}
// skip this block and search in successors chain
......@@ -342,6 +345,7 @@ public class IfMakerHelper {
if (next.getPredecessors().size() != 1) {
return null;
}
List<InsnNode> insns = block.getInstructions();
boolean pass = true;
if (!insns.isEmpty()) {
// check that all instructions can be inlined
......
......@@ -197,7 +197,6 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
}
// array for each loop confirmed
len.add(AFlag.DONT_GENERATE);
incrInsn.getResult().add(AFlag.DONT_GENERATE);
condArg.add(AFlag.DONT_GENERATE);
bCondArg.add(AFlag.DONT_GENERATE);
......@@ -208,6 +207,8 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
((RegisterArg) arrayArg).getSVar().removeUse((RegisterArg) arrGetInsn.getArg(0));
}
CodeShrinker.shrinkMethod(mth);
len.add(AFlag.DONT_GENERATE);
if (arrGetInsn.contains(AFlag.WRAPPED)) {
InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, arrGetInsn);
if (wrapArg != null && wrapArg.getParentInsn() != null) {
......
......@@ -7,9 +7,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBranchRegion;
......@@ -33,8 +30,6 @@ import jadx.core.utils.RegionUtils;
*/
public class ProcessTryCatchRegions extends AbstractRegionVisitor {
private static final Logger LOG = LoggerFactory.getLogger(ProcessTryCatchRegions.class);
public static void process(MethodNode mth) {
if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
return;
......
......@@ -115,8 +115,8 @@ public class RegionMaker {
}
}
if (!processed && block.getInstructions().size() == 1) {
InsnNode insn = block.getInstructions().get(0);
InsnNode insn = BlockUtils.getLastInsn(block);
if (!processed && insn != null) {
switch (insn.getType()) {
case IF:
next = processIf(r, block, (IfNode) insn, stack);
......@@ -247,9 +247,11 @@ public class RegionMaker {
*/
private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) {
for (BlockNode block : exitBlocks) {
if (block.contains(AType.EXC_HANDLER)
|| block.getInstructions().size() != 1
|| block.getInstructions().get(0).getType() != InsnType.IF) {
if (block.contains(AType.EXC_HANDLER)) {
continue;
}
InsnNode lastInsn = BlockUtils.getLastInsn(block);
if (lastInsn == null || lastInsn.getType() != InsnType.IF) {
continue;
}
List<LoopInfo> loops = block.getAll(AType.LOOP);
......@@ -533,6 +535,7 @@ public class RegionMaker {
insnBlock.add(AFlag.DONT_GENERATE);
}
exitInsn.add(AFlag.DONT_GENERATE);
exitInsn.add(AFlag.REMOVE);
InstructionRemover.unbindInsn(mth, exitInsn);
}
......
......@@ -54,7 +54,6 @@ public class RegionMakerVisitor extends AbstractVisitor {
mth.getRegion().add(expOutBlock);
}
}
postProcessRegions(mth);
}
......@@ -85,75 +84,76 @@ public class RegionMakerVisitor extends AbstractVisitor {
insertEdgeInsn((Region) region);
}
}
}
/**
* Insert insn block from edge insn attribute.
*/
private static void insertEdgeInsn(Region region) {
List<IContainer> subBlocks = region.getSubBlocks();
if (subBlocks.isEmpty()) {
return;
}
IContainer last = subBlocks.get(subBlocks.size() - 1);
List<EdgeInsnAttr> edgeInsnAttrs = last.getAll(AType.EDGE_INSN);
if (edgeInsnAttrs.isEmpty()) {
return;
}
EdgeInsnAttr insnAttr = edgeInsnAttrs.get(0);
if (!insnAttr.getStart().equals(last)) {
return;
}
List<InsnNode> insns = Collections.singletonList(insnAttr.getInsn());
region.add(new InsnContainer(insns));
}
private static void processSwitch(MethodNode mth, SwitchRegion sw) {
for (IContainer c : sw.getBranches()) {
if (!(c instanceof Region)) {
continue;
/**
* Insert insn block from edge insn attribute.
*/
private static void insertEdgeInsn(Region region) {
List<IContainer> subBlocks = region.getSubBlocks();
if (subBlocks.isEmpty()) {
return;
}
Set<IBlock> blocks = new HashSet<>();
RegionUtils.getAllRegionBlocks(c, blocks);
if (blocks.isEmpty()) {
addBreakToContainer((Region) c);
continue;
IContainer last = subBlocks.get(subBlocks.size() - 1);
List<EdgeInsnAttr> edgeInsnAttrs = last.getAll(AType.EDGE_INSN);
if (edgeInsnAttrs.isEmpty()) {
return;
}
for (IBlock block : blocks) {
if (!(block instanceof BlockNode)) {
continue;
}
BlockNode bn = (BlockNode) block;
for (BlockNode s : bn.getCleanSuccessors()) {
if (!blocks.contains(s)
&& !bn.contains(AFlag.ADDED_TO_REGION)
&& !s.contains(AFlag.FALL_THROUGH)) {
addBreak(mth, c, bn);
break;
EdgeInsnAttr insnAttr = edgeInsnAttrs.get(0);
if (!insnAttr.getStart().equals(last)) {
return;
}
List<InsnNode> insns = Collections.singletonList(insnAttr.getInsn());
region.add(new InsnContainer(insns));
}
private static void processSwitch(MethodNode mth, SwitchRegion sw) {
for (IContainer c : sw.getBranches()) {
if (c instanceof Region) {
Set<IBlock> blocks = new HashSet<>();
RegionUtils.getAllRegionBlocks(c, blocks);
if (blocks.isEmpty()) {
addBreakToContainer((Region) c);
} else {
for (IBlock block : blocks) {
if (block instanceof BlockNode) {
addBreakForBlock(mth, c, blocks, (BlockNode) block);
}
}
}
}
}
}
}
private static void addBreak(MethodNode mth, IContainer c, BlockNode bn) {
IContainer blockContainer = RegionUtils.getBlockContainer(c, bn);
if (blockContainer instanceof Region) {
addBreakToContainer((Region) blockContainer);
} else if (c instanceof Region) {
addBreakToContainer((Region) c);
} else {
LOG.warn("Can't insert break, container: {}, block: {}, mth: {}", blockContainer, bn, mth);
private static void addBreakToContainer(Region c) {
if (RegionUtils.hasExitEdge(c)) {
return;
}
List<InsnNode> insns = new ArrayList<>(1);
insns.add(new InsnNode(InsnType.BREAK, 0));
c.add(new InsnContainer(insns));
}
}
private static void addBreakToContainer(Region c) {
if (RegionUtils.hasExitEdge(c)) {
return;
private static void addBreakForBlock(MethodNode mth, IContainer c, Set<IBlock> blocks, BlockNode bn) {
for (BlockNode s : bn.getCleanSuccessors()) {
if (!blocks.contains(s)
&& !bn.contains(AFlag.ADDED_TO_REGION)
&& !s.contains(AFlag.FALL_THROUGH)) {
addBreak(mth, c, bn);
return;
}
}
}
private static void addBreak(MethodNode mth, IContainer c, BlockNode bn) {
IContainer blockContainer = RegionUtils.getBlockContainer(c, bn);
if (blockContainer instanceof Region) {
addBreakToContainer((Region) blockContainer);
} else if (c instanceof Region) {
addBreakToContainer((Region) c);
} else {
LOG.warn("Can't insert break, container: {}, block: {}, mth: {}", blockContainer, bn, mth);
}
}
List<InsnNode> insns = new ArrayList<>(1);
insns.add(new InsnNode(InsnType.BREAK, 0));
c.add(new InsnContainer(insns));
}
private static void removeSynchronized(MethodNode mth) {
......
......@@ -43,6 +43,10 @@ public class SSATransform extends AbstractVisitor {
}
private static void process(MethodNode mth) {
if (!mth.getSVars().isEmpty()) {
return;
}
LiveVarAnalysis la = new LiveVarAnalysis(mth);
la.runAnalysis();
int regsCount = mth.getRegsCount();
......@@ -63,6 +67,8 @@ public class SSATransform extends AbstractVisitor {
throw new JadxRuntimeException("Phi nodes fix limit reached!");
}
} while (repeatFix);
hidePhiInsns(mth);
}
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
......@@ -117,9 +123,6 @@ public class SSATransform extends AbstractVisitor {
}
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];
......@@ -432,4 +435,10 @@ public class SSATransform extends AbstractVisitor {
}
}
}
private static void hidePhiInsns(MethodNode mth) {
for (BlockNode block : mth.getBasicBlocks()) {
block.getInstructions().removeIf(insn -> insn.getType() == InsnType.PHI);
}
}
}
......@@ -114,6 +114,9 @@ public class TypeCompare {
if (unknown == ArgType.UNKNOWN_OBJECT && (known.isObject() || known.isArray())) {
return NARROW;
}
if (known.equals(ArgType.OBJECT) && unknown.isArray()) {
return WIDER;
}
PrimitiveType knownPrimitive;
if (known.isPrimitive()) {
knownPrimitive = known.getPrimitiveType();
......
......@@ -33,6 +33,7 @@ import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.trycatch.ExcHandlerAttr;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.ConstInlineVisitor;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.utils.Utils;
......@@ -131,7 +132,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
if (Consts.DEBUG) {
if (ssaVar.getTypeInfo().getType().equals(candidateType)) {
LOG.info("Same type rejected: {} -> {}, bounds: {}", ssaVar, candidateType, bounds);
} else {
} else if (candidateType.isTypeKnown()) {
LOG.debug("Type set rejected: {} -> {}, bounds: {}", ssaVar, candidateType, bounds);
}
}
......@@ -311,6 +312,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
for (InsnArg phiArg : phiInsn.getArguments()) {
mergePhiBounds(((RegisterArg) phiArg).getSVar());
}
InitCodeVariables.initCodeVar(newSsaVar);
return true;
}
}
......
......@@ -246,7 +246,6 @@ public final class TypeUpdate {
registry.put(InsnType.CONST, this::sameFirstArgListener);
registry.put(InsnType.MOVE, this::moveListener);
registry.put(InsnType.PHI, this::allSameListener);
registry.put(InsnType.MERGE, this::allSameListener);
registry.put(InsnType.AGET, this::arrayGetListener);
registry.put(InsnType.APUT, this::arrayPutListener);
registry.put(InsnType.IF, this::ifListener);
......
......@@ -107,9 +107,9 @@ public class DebugUtils {
CodeWriter code = new CodeWriter();
ig.makeInsn(insn, code);
String insnStr = code.toString().substring(CodeWriter.NL.length());
LOG.debug("{}> {}\t{}", indent, insnStr, insn.getAttributesString());
LOG.debug("{}|> {}\t{}", indent, insnStr, insn.getAttributesString());
} catch (CodegenException e) {
LOG.debug("{}>!! {}", indent, insn);
LOG.debug("{}|>!! {}", indent, insn);
}
}
}
......
......@@ -16,6 +16,7 @@ import static jadx.core.dex.instructions.args.ArgType.NARROW;
import static jadx.core.dex.instructions.args.ArgType.NARROW_INTEGRAL;
import static jadx.core.dex.instructions.args.ArgType.OBJECT;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_ARRAY;
import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_OBJECT;
import static jadx.core.dex.instructions.args.ArgType.array;
import static org.hamcrest.Matchers.is;
......@@ -60,6 +61,8 @@ public class TypeCompareTest {
firstIsNarrow(array(OBJECT), OBJECT);
firstIsNarrow(array(OBJECT), array(UNKNOWN_OBJECT));
firstIsNarrow(UNKNOWN_ARRAY, OBJECT);
}
@Test
......
......@@ -17,7 +17,6 @@ import java.util.jar.JarOutputStream;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess;
import jadx.core.Jadx;
import jadx.core.ProcessClass;
import jadx.core.codegen.CodeGen;
import jadx.core.dex.attributes.AFlag;
......@@ -29,7 +28,6 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.xmlgen.ResourceStorage;
import jadx.core.xmlgen.entry.ResourceEntry;
import jadx.tests.api.compiler.DynamicCompiler;
......@@ -141,34 +139,19 @@ public abstract class IntegrationTest extends TestUtils {
}
protected void decompile(JadxDecompiler jadx, ClassNode cls) {
List<IDexTreeVisitor> passes = getPassesList(jadx);
List<IDexTreeVisitor> passes = JadxInternalAccess.getPassList(jadx);
ProcessClass.process(cls, passes, new CodeGen());
}
protected void decompileWithoutUnload(JadxDecompiler jadx, ClassNode cls) {
cls.load();
List<IDexTreeVisitor> passes = getPassesList(jadx);
for (IDexTreeVisitor visitor : passes) {
for (IDexTreeVisitor visitor : JadxInternalAccess.getPassList(jadx)) {
DepthTraversal.visit(visitor, cls);
}
generateClsCode(cls);
// don't unload class
}
private List<IDexTreeVisitor> getPassesList(JadxDecompiler jadx) {
RootNode root = JadxInternalAccess.getRoot(jadx);
List<IDexTreeVisitor> passesList = Jadx.getPassesList(jadx.getArgs());
passesList.forEach(pass -> {
try {
pass.init(root);
} catch (JadxException e) {
e.printStackTrace();
fail(e.getMessage());
}
});
return passesList;
}
protected void generateClsCode(ClassNode cls) {
try {
new CodeGen().visit(cls);
......@@ -442,6 +425,11 @@ public abstract class IntegrationTest extends TestUtils {
protected void setOutputCFG() {
this.args.setCfgOutput(true);
this.args.setRawCFGOutput(true);
} // Use only for debug purpose
@Deprecated
protected void setOutputRawCFG() {
this.args.setRawCFGOutput(true);
}
// Use only for debug purpose
......
......@@ -4,6 +4,10 @@ import jadx.core.codegen.CodeWriter;
public class TestUtils {
public static String indent() {
return CodeWriter.INDENT_STR;
}
public static String indent(int indent) {
if (indent == 1) {
return CodeWriter.INDENT_STR;
......@@ -24,5 +28,4 @@ public class TestUtils {
}
return count;
}
}
......@@ -32,8 +32,8 @@ public class TestFieldIncrement2 extends IntegrationTest {
String code = cls.getCode().toString();
assertThat(code, containsString("this.a.f += n;"));
assertThat(code, containsString("a.f *= n;"));
// TODO
// assertThat(code, containsString("this.a.f *= n;"));
assertThat(code, containsString("a2.f *= n;"));
// TODO:
// assertThat(code, containsString("this.a.f *= n;"));
}
}
......@@ -6,19 +6,24 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class TestTernary2 extends IntegrationTest {
public static class TestCls {
public void test() {
assertTrue(f(1, 0) == 0);
checkFalse(f(1, 0) == 0);
}
private int f(int a, int b) {
return a + b;
}
private void checkFalse(boolean b) {
if (b) {
throw new AssertionError("Must be false");
}
}
}
@Test
......@@ -26,9 +31,8 @@ public class TestTernary2 extends IntegrationTest {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertEquals(1, count(code, "assertTrue"));
assertEquals(1, count(code, "f(1, 0)"));
// TODO:
// assertThat(code, containsString("assertTrue(f(1, 0) == 0);"));
// assertThat(code, containsString("checkFalse(f(1, 0) == 0);"));
}
}
......@@ -24,8 +24,8 @@ public class TestEnums4 extends IntegrationTest {
private final String[] exts;
private ResType(String... exts) {
this.exts = exts;
private ResType(String... extensions) {
this.exts = extensions;
}
public String[] getExts() {
......@@ -44,7 +44,7 @@ public class TestEnums4 extends IntegrationTest {
String code = cls.getCode().toString();
assertThat(code, containsOne("CODE(\".dex\", \".class\"),"));
assertThat(code, containsOne("ResType(String... exts) {"));
assertThat(code, containsOne("ResType(String... extensions) {"));
// assertThat(code, not(containsString("private ResType")));
}
}
......@@ -19,9 +19,9 @@ public class TestGenerics2 extends IntegrationTest {
private static class ItemReference<V> extends WeakReference<V> {
private Object id;
public ItemReference(V item, Object id, ReferenceQueue<? super V> queue) {
public ItemReference(V item, Object objId, ReferenceQueue<? super V> queue) {
super(item, queue);
this.id = id;
this.id = objId;
}
}
......@@ -43,7 +43,7 @@ public class TestGenerics2 extends IntegrationTest {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsString("public ItemReference(V item, Object id, ReferenceQueue<? super V> queue) {"));
assertThat(code, containsString("public ItemReference(V item, Object objId, ReferenceQueue<? super V> queue) {"));
assertThat(code, containsString("public V get(Object id) {"));
assertThat(code, containsString("WeakReference<V> ref = "));
assertThat(code, containsString("return ref.get();"));
......
package jadx.tests.integration.trycatch;
import java.io.IOException;
import org.junit.Test;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class TestFinallyExtract extends IntegrationTest {
public static class TestCls {
private int result = 0;
public String test() throws IOException {
public String test() {
boolean success = false;
try {
String value = test();
String value = call();
result++;
success = true;
return value;
} finally {
if (!success) {
test();
result -= 2;
}
}
}
private String call() {
return "call";
}
public void check() {
test();
assertEquals(result, 1);
}
}
@Test
......@@ -33,10 +45,38 @@ public class TestFinallyExtract extends IntegrationTest {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, not(containsString("if (0 == 0) {")));
assertThat(code, containsOne("boolean success = false;"));
assertThat(code, containsOne("try {"));
assertThat(code, containsOne("success = true;"));
assertThat(code, containsOne("return value;"));
assertThat(code, containsOne("try {"));
assertThat(code, containsOne("} finally {"));
assertThat(code, containsOne("if (!success) {"));
}
@Test
public void testNoDebug() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
// java compiler optimization: 'success' variable completely removed and no code duplication:
/*
public String test() {
try {
String call = call();
this.result++;
return call;
} catch (Throwable th) {
this.result -= 2;
throw th;
}
}
*/
assertThat(code, containsOne("this.result++;"));
assertThat(code, containsOne("} catch (Throwable th) {"));
assertThat(code, containsOne("this.result -= 2;"));
assertThat(code, containsOne("throw th;"));
}
}
......@@ -24,13 +24,23 @@ public class TestTryCatch7 extends IntegrationTest {
}
@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
check(code, "e", "ex");
}
@Test
public void testNoDebug() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
String excVarName = "exception";
String catchExcVarName = "e";
check(code, "e", "e2");
}
private void check(String code, String excVarName, String catchExcVarName) {
assertThat(code, containsOne("Exception " + excVarName + " = new Exception();"));
assertThat(code, containsOne("} catch (Exception " + catchExcVarName + ") {"));
assertThat(code, containsOne(excVarName + " = " + catchExcVarName + ";"));
......
......@@ -31,8 +31,8 @@ public class TestTryCatch8 extends IntegrationTest {
synchronized (this) {
try {
throw new MyException();
} catch (MyException e) {
this.e = e;
} catch (MyException myExc) {
this.e = myExc;
} catch (Exception x) {
this.e = new MyException("MyExc", x);
}
......@@ -54,9 +54,19 @@ public class TestTryCatch8 extends IntegrationTest {
assertThat(code, containsOne("synchronized (this) {"));
assertThat(code, containsOne("throw new MyException();"));
assertThat(code, containsOne("} catch (MyException e) {"));
assertThat(code, containsOne("this.e = e;"));
assertThat(code, containsOne("} catch (MyException myExc) {"));
assertThat(code, containsOne("this.e = myExc;"));
assertThat(code, containsOne("} catch (Exception x) {"));
assertThat(code, containsOne("this.e = new MyException(\"MyExc\", x);"));
}
@Test
public void testNoDebug() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsOne("synchronized (this) {"));
assertThat(code, containsOne("throw new MyException();"));
}
}
......@@ -18,6 +18,7 @@ public class TestTryCatchFinally6 extends IntegrationTest {
public static void test() throws IOException {
InputStream is = null;
try {
call();
is = new FileInputStream("1.txt");
} finally {
if (is != null) {
......@@ -25,6 +26,9 @@ public class TestTryCatchFinally6 extends IntegrationTest {
}
}
}
private static void call() {
}
}
@Test
......@@ -35,6 +39,7 @@ public class TestTryCatchFinally6 extends IntegrationTest {
assertThat(code, containsLines(2,
"InputStream is = null;",
"try {",
indent(1) + "call();",
indent(1) + "is = new FileInputStream(\"1.txt\");",
"} finally {",
indent(1) + "if (is != null) {",
......@@ -43,4 +48,23 @@ public class TestTryCatchFinally6 extends IntegrationTest {
"}"
));
}
@Test
public void testNoDebug() {
noDebugInfo();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
assertThat(code, containsLines(2,
"FileInputStream fileInputStream = null;",
"try {",
indent() + "call();",
indent() + "fileInputStream = new FileInputStream(\"1.txt\");",
"} finally {",
indent() + "if (fileInputStream != null) {",
indent() + indent() + "fileInputStream.close();",
indent() + "}",
"}"
));
}
}
......@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class TestTryCatch3 extends IntegrationTest {
public class TestTryCatchFinally7 extends IntegrationTest {
public static class TestCls {
private int f = 0;
......
......@@ -4,6 +4,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.junit.Ignore;
import org.junit.Test;
import jadx.core.dex.nodes.ClassNode;
......@@ -12,7 +13,7 @@ import jadx.tests.api.IntegrationTest;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
public class TestTryCatch5 extends IntegrationTest {
public class TestTryCatchFinally8 extends IntegrationTest {
public static class TestCls {
private Object test(Object obj) {
......@@ -41,9 +42,9 @@ public class TestTryCatch5 extends IntegrationTest {
}
}
@Ignore("Fix merged catch blocks (shared code between catches)")
@Test
public void test() {
disableCompilation();
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();
......
......@@ -8,7 +8,7 @@ import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.junit.Assert.assertThat;
public class TestTryCatchNoMove extends SmaliTest {
public class TestTryCatchNoMoveExc extends SmaliTest {
// private static void test(AutoCloseable closeable) {
// if (closeable != null) {
......@@ -21,7 +21,7 @@ public class TestTryCatchNoMove extends SmaliTest {
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("trycatch", "TestTryCatchNoMove");
ClassNode cls = getClassNodeFromSmaliWithPkg("trycatch", "TestTryCatchNoMoveExc");
String code = cls.getCode().toString();
assertThat(code, containsOne("if (autoCloseable != null) {"));
......
......@@ -26,7 +26,7 @@ public class TestTryCatchNoMoveExc2 extends SmaliTest {
@Test
public void test() {
ClassNode cls = getClassNodeFromSmaliWithPath("trycatch", "TestTryCatchNoMoveExc2");
ClassNode cls = getClassNodeFromSmaliWithPkg("trycatch", "TestTryCatchNoMoveExc2");
String code = cls.getCode().toString();
assertThat(code, containsOne("try {"));
......
.class public LTestTryCatchNoMove;
.class public Ltrycatch/TestTryCatchNoMoveExc;
.super Ljava/lang/Object;
.method private static test(Ljava/lang/AutoCloseable;)V
......
.class public LTestTryCatchNoMoveExc2;
.class public Ltrycatch/TestTryCatchNoMoveExc2;
.super Ljava/lang/Object;
.method private static test(Ljava/lang/AutoCloseable;)V
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment