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;
}
......
......@@ -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));
......
......@@ -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