fix: prefer early return for 'if-else-if' block (#2052)

This commit is contained in:
Skylot
2023-12-05 20:33:05 +00:00
parent 5d56001826
commit 2d5c0fda4a
6 changed files with 673 additions and 37 deletions
@@ -32,7 +32,6 @@ import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.SwitchRegion;
import jadx.core.dex.regions.SwitchRegion.CaseInfo;
import jadx.core.dex.regions.SynchronizedRegion;
@@ -147,15 +146,12 @@ public class RegionGen extends InsnGen {
* Connect if-else-if block
*/
private boolean connectElseIf(ICodeWriter code, IContainer els) throws CodegenException {
if (els.contains(AFlag.ELSE_IF_CHAIN) && els instanceof Region) {
List<IContainer> subBlocks = ((Region) els).getSubBlocks();
if (subBlocks.size() == 1) {
IContainer elseBlock = subBlocks.get(0);
if (elseBlock instanceof IfRegion) {
declareVars(code, elseBlock);
makeIf((IfRegion) elseBlock, code, false);
return true;
}
if (els.contains(AFlag.ELSE_IF_CHAIN)) {
IContainer elseBlock = RegionUtils.getSingleSubBlock(els);
if (elseBlock instanceof IfRegion) {
declareVars(code, elseBlock);
makeIf((IfRegion) elseBlock, code, false);
return true;
}
}
return false;
@@ -13,6 +13,7 @@ import jadx.core.dex.regions.conditions.IfCondition;
import jadx.core.dex.regions.conditions.IfCondition.Mode;
import jadx.core.dex.regions.conditions.IfRegion;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.RegionUtils;
import static jadx.core.utils.RegionUtils.insnsCount;
@@ -81,21 +82,23 @@ public class IfRegionVisitor extends AbstractVisitor {
return;
}
}
boolean lastRegion = RegionUtils.hasExitEdge(ifRegion);
if (elseSize == 1 && lastRegion && mth.isVoidReturn()) {
InsnNode lastElseInsn = RegionUtils.getLastInsn(ifRegion.getElseRegion());
if (lastElseInsn != null && lastElseInsn.getType() == InsnType.THROW) {
// move `throw` into `then` block
invertIfRegion(ifRegion);
} else {
// single return at method end will be removed later
if (elseSize == 1) {
boolean lastRegion = RegionUtils.hasExitEdge(ifRegion);
if (lastRegion && mth.isVoidReturn()) {
InsnNode lastElseInsn = RegionUtils.getLastInsn(ifRegion.getElseRegion());
if (InsnUtils.isInsnType(lastElseInsn, InsnType.THROW)) {
// move `throw` into `then` block
invertIfRegion(ifRegion);
} else {
// single return at method end will be removed later
}
return;
}
if (thenSize > 2 && !(lastRegion && thenSize < 4 /* keep small code block inside else */)) {
invertIfRegion(ifRegion);
return;
}
return;
}
if (!lastRegion) {
invertIfRegion(ifRegion);
}
return;
}
boolean thenExit = RegionUtils.hasExitBlock(ifRegion.getThenRegion());
boolean elseExit = RegionUtils.hasExitBlock(ifRegion.getElseRegion());
@@ -155,22 +158,32 @@ public class IfRegionVisitor extends AbstractVisitor {
}
}
@SuppressWarnings("StatementWithEmptyBody")
private static boolean removeRedundantElseBlock(MethodNode mth, IfRegion ifRegion) {
if (ifRegion.getElseRegion() == null
|| ifRegion.contains(AFlag.ELSE_IF_CHAIN)
|| ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN)) {
if (ifRegion.getElseRegion() == null) {
return false;
}
if (!RegionUtils.hasExitBlock(ifRegion.getThenRegion())) {
return false;
}
// code style check:
// will remove 'return;' from 'then' and 'else' with one instruction
// see #jadx.tests.integration.conditions.TestConditions9
if (mth.isVoidReturn()
&& insnsCount(ifRegion.getThenRegion()) == 2
&& insnsCount(ifRegion.getElseRegion()) == 2) {
return false;
InsnNode lastThanInsn = RegionUtils.getLastInsn(ifRegion.getThenRegion());
if (InsnUtils.isInsnType(lastThanInsn, InsnType.THROW)) {
// always omit else after 'throw'
} else {
// code style check:
// will remove 'return;' from 'then' and 'else' with one instruction
// see #jadx.tests.integration.conditions.TestConditions9
if (mth.isVoidReturn()) {
int thenSize = insnsCount(ifRegion.getThenRegion());
// keep small blocks with same or 'similar' size unchanged
if (thenSize < 5) {
int elseSize = insnsCount(ifRegion.getElseRegion());
int range = ifRegion.getElseRegion().contains(AFlag.ELSE_IF_CHAIN) ? 4 : 2;
if (thenSize == elseSize || (thenSize * range > elseSize && thenSize < elseSize * range)) {
return false;
}
}
}
}
IRegion parent = ifRegion.getParent();
Region newRegion = new Region(parent);
@@ -324,6 +324,30 @@ public class RegionUtils {
}
}
public static IContainer getSingleSubBlock(IContainer container) {
if (container instanceof Region) {
List<IContainer> subBlocks = ((Region) container).getSubBlocks();
if (subBlocks.size() == 1) {
return ignoreSimpleRegionWrapper(subBlocks.get(0));
}
}
return null;
}
private static IContainer ignoreSimpleRegionWrapper(IContainer container) {
while (true) {
if (container instanceof Region) {
List<IContainer> subBlocks = ((Region) container).getSubBlocks();
if (subBlocks.size() != 1) {
return container;
}
container = subBlocks.get(0);
} else {
return container;
}
}
}
public static List<IContainer> getExcHandlersForRegion(IContainer region) {
TryCatchBlockAttr tb = region.get(AType.TRY_BLOCK);
if (tb != null) {