Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import jadx.core.dex.regions.loops.LoopRegion;
import jadx.core.dex.regions.loops.LoopType;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.Utils;
Expand Down Expand Up @@ -365,11 +366,18 @@ private void makeCatchBlock(ICodeWriter code, ExceptionHandler handler) throws C
if (arg == null) {
code.add("unknown"); // throwing exception is too late at this point
} else if (arg instanceof RegisterArg) {
SSAVar ssaVar = ((RegisterArg) arg).getSVar();
if (code.isMetadataSupported()) {
RegisterArg regArg = (RegisterArg) arg;
SSAVar ssaVar = regArg.getSVar();
if (ssaVar == null) {
InitCodeVariables.initCodeVar(mth, regArg);
ssaVar = regArg.getSVar();
} else if (!ssaVar.isCodeVarSet()) {
InitCodeVariables.initCodeVar(ssaVar);
}
if (ssaVar != null && code.isMetadataSupported()) {
code.attachDefinition(VarNode.get(mth, ssaVar));
}
code.add(mgen.getNameGen().assignArg(ssaVar.getCodeVar()));
code.add(ssaVar != null ? mgen.getNameGen().assignArg(ssaVar.getCodeVar()) : "unknown");
} else if (arg instanceof NamedArg) {
code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));
} else {
Expand Down
14 changes: 13 additions & 1 deletion jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public SwitchRegion(IRegion parent, BlockNode header) {

public static final class CaseInfo {
private final List<Object> keys;
private final IContainer container;
private IContainer container;

public CaseInfo(List<Object> keys, IContainer container) {
this.keys = keys;
Expand Down Expand Up @@ -85,6 +85,18 @@ public List<IContainer> getBranches() {
return Collections.unmodifiableList(getCaseContainers());
}

@Override
public boolean replaceSubBlock(IContainer oldBlock, IContainer newBlock) {
for (CaseInfo caseInfo : cases) {
if (caseInfo.container == oldBlock) {
caseInfo.container = newBlock;
updateParent(newBlock, this);
return true;
}
}
return false;
}

@Override
public void generate(RegionGen regionGen, ICodeWriter code) throws CodegenException {
regionGen.makeSwitch(this, code);
Expand Down
30 changes: 26 additions & 4 deletions jadx-core/src/main/java/jadx/core/dex/visitors/ModVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,10 @@ private static void removeCheckCast(MethodNode mth, BlockNode block, int i, Inde
ArgType castType = (ArgType) insn.getIndex();
if (!ArgType.isCastNeeded(mth.root(), castArg.getType(), castType)) {
RegisterArg result = insn.getResult();
if (!canUpdateCastResultType(result, castArg.getType())) {
insn.add(AFlag.EXPLICIT_CAST);
return;
}
result.setType(castArg.getType());

InsnNode move = new InsnNode(InsnType.MOVE, 1);
Expand All @@ -432,6 +436,14 @@ private static void removeCheckCast(MethodNode mth, BlockNode block, int i, Inde
}
}

private static boolean canUpdateCastResultType(@Nullable RegisterArg result, ArgType type) {
if (result == null || result.getSVar() == null) {
return false;
}
ArgType immutableType = result.getImmutableType();
return immutableType == null || Objects.equals(immutableType, type);
}

private static @Nullable InsnNode isCastDuplicate(IndexInsnNode castInsn) {
InsnArg arg = castInsn.getArg(0);
if (arg.isRegister()) {
Expand Down Expand Up @@ -520,19 +532,29 @@ private static void processAnonymousConstructor(MethodNode mth, ConstructorInsn
int argsCount = Math.min(callMth.getMethodInfo().getArgsCount(), co.getArgsCount());
for (int i = 0; i < argsCount; i++) {
if (attr.isSkip(i)) {
anonymousCallArgMod(co.getArg(i));
anonymousCallArgMod(mth, co.getArg(i));
}
}
} else {
// additional info not available apply mods to all args (the safest solution)
co.getArguments().forEach(ModVisitor::anonymousCallArgMod);
co.getArguments().forEach(arg -> anonymousCallArgMod(mth, arg));
}
}

private static void anonymousCallArgMod(InsnArg arg) {
private static void anonymousCallArgMod(MethodNode mth, InsnArg arg) {
arg.add(AFlag.DONT_INLINE);
if (arg.isRegister()) {
((RegisterArg) arg).getSVar().getCodeVar().setFinal(true);
RegisterArg regArg = (RegisterArg) arg;
SSAVar ssaVar = regArg.getSVar();
if (ssaVar == null) {
InitCodeVariables.initCodeVar(mth, regArg);
ssaVar = regArg.getSVar();
} else if (!ssaVar.isCodeVarSet()) {
InitCodeVariables.initCodeVar(ssaVar);
}
if (ssaVar != null) {
ssaVar.getCodeVar().setFinal(true);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ private static void removeInstructions(BlockNode block) {
case MOVE:
// remove redundant moves: unused result and same args names (a = a;)
RegisterArg result = insn.getResult();
if (result == null) {
if (!insn.getArg(0).isInsnWrap()) {
it.remove();
}
break;
}
if (result.getSVar().getUseCount() == 0
&& result.isNameEquals(insn.getArg(0))) {
it.remove();
Expand Down Expand Up @@ -254,7 +260,7 @@ private static void modifyArith(BlockNode block) {
* Otherwise, move to the top and add a warning.
*/
private void moveConstructorInConstructor(MethodNode mth) {
if (!mth.isConstructor()) {
if (!mth.isConstructor() || mth.getRegion() == null) {
return;
}
ConstructorInsn ctrInsn = searchConstructorCall(mth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ private static void checkForUnreachableBlocks(MethodNode mth) {
fixed = true;
break;
}
throw new JadxRuntimeException("Unreachable block: " + block);
removeUnreachableBlock(block, mth);
fixed = true;
break;
}
}
if (!fixed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import jadx.core.dex.visitors.regions.maker.SynchronizedRegionMaker;
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxOverflowException;

@JadxVisitor(
name = "RegionMakerVisitor",
Expand All @@ -21,17 +22,25 @@ public void visit(MethodNode mth) throws JadxException {
if (mth.isNoCode() || mth.getBasicBlocks().isEmpty()) {
return;
}
RegionMaker rm = new RegionMaker(mth);
mth.setRegion(rm.makeMthRegion());
if (!mth.isNoExceptionHandlers()) {
new ExcHandlersRegionMaker(mth, rm).process();
}
processForceInlineInsns(mth);
ProcessTryCatchRegions.process(mth);
PostProcessRegions.process(mth);
CleanRegions.process(mth);
if (mth.getAccessFlags().isSynchronized()) {
SynchronizedRegionMaker.removeSynchronized(mth);
try {
RegionMaker rm = new RegionMaker(mth);
mth.setRegion(rm.makeMthRegion());
if (!mth.isNoExceptionHandlers()) {
new ExcHandlersRegionMaker(mth, rm).process();
}
processForceInlineInsns(mth);
ProcessTryCatchRegions.process(mth);
PostProcessRegions.process(mth);
CleanRegions.process(mth);
if (mth.getAccessFlags().isSynchronized()) {
SynchronizedRegionMaker.removeSynchronized(mth);
}
} catch (JadxOverflowException e) {
mth.setRegion(null);
mth.addWarnComment("Code restructure failed: " + e.getMessage() + ", using fallback mode");
} catch (StackOverflowError e) {
mth.setRegion(null);
mth.addWarnComment("Code restructure failed: StackOverflowError, using fallback mode", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public boolean enterRegion(MethodNode mth, IRegion region) {
public void leaveRegion(MethodNode mth, IRegion region) {
if (addBreakRegion.contains(region)) {
addBreakRegion.remove(region);
region.getSubBlocks().add(SwitchRegionMaker.buildBreakContainer(currentSwitch));
SwitchRegionMaker.appendBreak(mth, region, currentSwitch);
}
if (cleanupSet.contains(region)) {
cleanupSet.remove(region);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ private static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockN
// forcing ternary inline for constructors (will help in moving super call to the top) and enums
// skip code style checks
} else {
if (elseAssign != null && elseAssign.isConstInsn()) {
if (canInlineConstAssign(elseAssign)) {
if (!verifyLineHints(mth, insn, elseAssign)) {
return;
}
Expand All @@ -330,7 +330,7 @@ private static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockN
return;
}
InsnArg elseArg;
if (elseAssign != null && elseAssign.isConstInsn()) {
if (canInlineConstAssign(elseAssign)) {
// inline constant
elseArg = InsnArg.wrapInsnIntoArg(elseAssign.copyWithoutResult());
SSAVar elseVar = elseAssign.getResult().getSVar();
Expand Down Expand Up @@ -359,6 +359,10 @@ private static void replaceWithTernary(MethodNode mth, IfRegion ifRegion, BlockN
CodeShrinkVisitor.shrinkMethod(mth);
}

private static boolean canInlineConstAssign(InsnNode insn) {
return insn != null && insn.isConstInsn() && insn.getResult() != null;
}

private TernaryMod() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ public void process() {
BlockNode handlerBlock = handler.getHandlerBlock();
if (handlerBlock != null) {
blocks.add(handlerBlock);
splitters.add(BlockUtils.getTopSplitterForHandler(handlerBlock));
BlockNode splitter = BlockUtils.searchTopSplitterForHandler(handlerBlock);
if (splitter != null) {
splitters.add(splitter);
} else {
mth.addDebugComment("Can't find top splitter block for handler: " + handlerBlock);
}
} else {
mth.addDebugComment("No exception handler block: " + handler);
}
Expand Down Expand Up @@ -124,7 +129,11 @@ private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
RegionStack stack = regionMaker.getStack().clear();
BlockNode dom;
if (handler.isFinally()) {
dom = BlockUtils.getTopSplitterForHandler(start);
dom = BlockUtils.searchTopSplitterForHandler(start);
if (dom == null) {
mth.addDebugComment("Can't find top splitter block for finally handler: " + start);
dom = start;
}
} else {
dom = start;
stack.addExits(exits);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,13 @@ private BlockNode getOneIntersectionBlock(BlockNode out, BitSet caseBlocks, BitS
outs.clear(mth.getExitBlock().getId());

BlockNode out = null;
LoopInfo loop = mth.getLoopForBlock(block);
if (outs.cardinality() == 1) {
// single exit
out = BlockUtils.bitSetToOneBlock(mth, outs);
} else {
// several switch exits
// possible 'return', 'continue' or fallthrough in one of the cases
LoopInfo loop = mth.getLoopForBlock(block);
if (loop != null) {
outs.andNot(loop.getStart().getPostDoms());
outs.andNot(loop.getEnd().getPostDoms());
Expand Down Expand Up @@ -220,12 +220,27 @@ private BlockNode getOneIntersectionBlock(BlockNode out, BitSet caseBlocks, BitS
// 'out' block already processed, prevent endless loop
// in this case it might be that 'out' is the LOOP_START of a loop and occurs before 'block'
// just try the immediate post dominator as a fallback
mth.addWarnComment("Switch 'out' block " + out + " for " + block + " already processed. Defaulting to fallback option.");
out = block.getIPostDom();
BlockNode fallbackOut = block.getIPostDom();
if (isLoopBackEdgeOut(loop, out, fallbackOut)) {
// Dominance frontier can point to the current loop header instead of the switch exit.
out = fallbackOut;
} else {
mth.addWarnComment("Switch 'out' block " + out + " for " + block
+ " already processed. Defaulting to fallback option.");
out = fallbackOut;
}
}
return out;
}

private boolean isLoopBackEdgeOut(@Nullable LoopInfo loop, BlockNode out, @Nullable BlockNode fallbackOut) {
return loop != null
&& out == loop.getStart()
&& fallbackOut != null
&& fallbackOut != out
&& !regionMaker.isProcessed(fallbackOut);
}

private BlockNode allSameReturns(RegionStack stack) {
BlockNode exitBlock = mth.getExitBlock();
List<BlockNode> preds = exitBlock.getPredecessors();
Expand Down Expand Up @@ -416,7 +431,7 @@ public void leaveRegion(MethodNode mth, IRegion region) {
}
}
if (insertBreak && canAppendBreak(region)) {
region.getSubBlocks().add(buildBreakContainer(switchRegion));
appendBreak(mth, region, switchRegion);
}
}
});
Expand All @@ -426,6 +441,30 @@ public static boolean canAppendBreak(IRegion region) {
return !region.contains(AFlag.FALL_THROUGH) && !RegionUtils.hasExitBlock(region);
}

public static boolean appendBreak(MethodNode mth, IRegion region, SwitchRegion switchRegion) {
return appendToRegion(mth, region, buildBreakContainer(switchRegion));
}

private static boolean appendToRegion(MethodNode mth, IRegion region, IContainer container) {
if (region instanceof Region) {
((Region) region).add(container);
return true;
}
IRegion parent = region.getParent();
if (parent == null) {
mth.addWarnComment("Failed to append 'break' to region without parent");
return false;
}
Region wrapper = new Region(parent);
if (!parent.replaceSubBlock(region, wrapper)) {
mth.addWarnComment("Failed to append 'break' to region: parent doesn't support replacement");
return false;
}
wrapper.add(region);
wrapper.add(container);
return true;
}

public static InsnContainer buildBreakContainer(SwitchRegion switchRegion) {
InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);
breakInsn.add(AFlag.SYNTHETIC);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.dex.visitors.JadxVisitor;

@JadxVisitor(
Expand All @@ -20,6 +21,9 @@ public void visit(MethodNode mth) {
return;
}
mth.getSVars().forEach(var -> {
if (!var.isCodeVarSet()) {
InitCodeVariables.initCodeVar(var);
}
ArgType type = var.getTypeInfo().getType();
if (!type.isTypeKnown()) {
mth.addWarnComment("Type inference failed for: " + var.getDetailedVarInfo(mth));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ public void visit(MethodNode mth) {
break;
}
}
} catch (JadxOverflowException e) {
mth.addWarnComment("Type fixes stopped after reaching update limit: " + e.getMessage());
} catch (Exception e) {
mth.addError("Types fix failed", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public void visit(MethodNode mth) {
runTypePropagation(mth);
} catch (StackOverflowError | BootstrapMethodError e) {
mth.addError("Type inference failed with stack overflow", new JadxOverflowException(e.getMessage()));
} catch (JadxOverflowException e) {
mth.addWarnComment("Type inference stopped after reaching update limit: " + e.getMessage());
} catch (Exception e) {
mth.addError("Type inference failed", e);
}
Expand Down
Loading