fix: handle empty endless loop (#1611)
This commit is contained in:
@@ -91,6 +91,19 @@ public class LoopInfo {
|
||||
this.parentLoop = parentLoop;
|
||||
}
|
||||
|
||||
public boolean hasParent(LoopInfo searchLoop) {
|
||||
LoopInfo parent = parentLoop;
|
||||
while (true) {
|
||||
if (parent == null) {
|
||||
return false;
|
||||
}
|
||||
if (parent == searchLoop) {
|
||||
return true;
|
||||
}
|
||||
parent = parent.getParentLoop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LOOP:" + id + ": " + start + "->" + end;
|
||||
|
||||
@@ -49,6 +49,10 @@ public final class LoopRegion extends ConditionRegion {
|
||||
return header;
|
||||
}
|
||||
|
||||
public boolean isEndless() {
|
||||
return header == null;
|
||||
}
|
||||
|
||||
public IRegion getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -391,7 +391,8 @@ public class BlockSplitter extends AbstractVisitor {
|
||||
&& block.getSuccessors().size() <= 1
|
||||
&& !block.getPredecessors().isEmpty()
|
||||
&& !block.contains(AFlag.MTH_ENTER_BLOCK)
|
||||
&& !block.contains(AFlag.MTH_EXIT_BLOCK);
|
||||
&& !block.contains(AFlag.MTH_EXIT_BLOCK)
|
||||
&& !block.getSuccessors().contains(block); // no self loop
|
||||
}
|
||||
|
||||
static void collectSuccessors(BlockNode startBlock, BlockNode methodEnterBlock, Set<BlockNode> toRemove) {
|
||||
|
||||
@@ -8,6 +8,7 @@ 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.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
|
||||
public class CleanRegions extends AbstractVisitor {
|
||||
@@ -42,6 +43,13 @@ public class CleanRegions extends AbstractVisitor {
|
||||
BlockNode block = (BlockNode) container;
|
||||
return block.getInstructions().isEmpty();
|
||||
}
|
||||
if (container instanceof LoopRegion) {
|
||||
LoopRegion loopRegion = (LoopRegion) container;
|
||||
if (loopRegion.isEndless()) {
|
||||
// keep empty endless loops
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (container instanceof IRegion) {
|
||||
List<IContainer> subBlocks = ((IRegion) container).getSubBlocks();
|
||||
for (IContainer subBlock : subBlocks) {
|
||||
|
||||
@@ -464,7 +464,7 @@ public class RegionMaker {
|
||||
BlockNode exitEnd = BlockUtils.followEmptyPath(exit);
|
||||
List<LoopInfo> loops = exitEnd.getAll(AType.LOOP);
|
||||
for (LoopInfo loopAtEnd : loops) {
|
||||
if (loopAtEnd != loop) {
|
||||
if (loopAtEnd != loop && loop.hasParent(loopAtEnd)) {
|
||||
insertEdge = exitEdge;
|
||||
confirm = true;
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,7 @@ import jadx.core.dex.nodes.IRegion;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.regions.loops.LoopRegion;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.trycatch.ExceptionHandler;
|
||||
import jadx.core.dex.trycatch.TryCatchBlockAttr;
|
||||
@@ -289,7 +290,11 @@ public class RegionUtils {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (container instanceof IRegion) {
|
||||
}
|
||||
if (container instanceof LoopRegion) {
|
||||
return true;
|
||||
}
|
||||
if (container instanceof IRegion) {
|
||||
IRegion region = (IRegion) container;
|
||||
for (IContainer block : region.getSubBlocks()) {
|
||||
if (notEmpty(block)) {
|
||||
@@ -297,9 +302,8 @@ public class RegionUtils {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
throw new JadxRuntimeException(unknownContainerType(container));
|
||||
}
|
||||
throw new JadxRuntimeException(unknownContainerType(container));
|
||||
}
|
||||
|
||||
public static void getAllRegionBlocks(IContainer container, Set<IBlock> blocks) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package jadx.tests.integration.loops;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
/**
|
||||
* Empty endless loop, issue #1611
|
||||
*/
|
||||
public class TestEndlessLoop2 extends SmaliTest {
|
||||
@Test
|
||||
public void test() {
|
||||
disableCompilation();
|
||||
assertThat(getClassNodeFromSmali())
|
||||
.code()
|
||||
.countString(2, "while (true) {");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
.class Lloops/TestEndlessLoop2;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.field instanceCount:J
|
||||
|
||||
.method test([Ljava/lang/String;)V
|
||||
.registers 10
|
||||
|
||||
const/16 p1, 0xb
|
||||
invoke-virtual {p0, p1}, Lloops/TestEndlessLoop2;->vMeth(I)V
|
||||
const/16 v0, 0xf1
|
||||
const-wide/high16 v1, 0x4032000000000000L # 18.0
|
||||
|
||||
:goto_a
|
||||
const-wide/high16 v3, 0x4076000000000000L # 352.0
|
||||
const/4 v5, 0x1
|
||||
cmpg-double v6, v1, v3
|
||||
if-gez v6, :cond_1c
|
||||
const/4 v0, 0x1
|
||||
|
||||
:goto_12
|
||||
add-int/2addr v0, v5
|
||||
const/16 v3, 0x4b
|
||||
if-ge v0, v3, :cond_18
|
||||
goto :goto_12
|
||||
|
||||
:cond_18
|
||||
const-wide/high16 v3, 0x3ff0000000000000L # 1.0
|
||||
add-double/2addr v1, v3
|
||||
goto :goto_a
|
||||
|
||||
:cond_1c
|
||||
iget-wide v3, p0, Lloops/TestEndlessLoop2;->instanceCount:J
|
||||
long-to-int v4, v3
|
||||
const/16 v3, 0xb
|
||||
|
||||
:goto_21
|
||||
const/16 v6, 0xf3
|
||||
|
||||
if-ge v5, v6, :cond_41
|
||||
rem-int/lit8 v6, v5, 0x9
|
||||
add-int/lit8 v6, v6, 0x12
|
||||
if-eq v6, p1, :cond_3e
|
||||
const/16 v7, 0x15
|
||||
if-eq v6, v7, :cond_36
|
||||
const/16 v7, 0x16
|
||||
if-eq v6, v7, :cond_34
|
||||
goto :goto_3b
|
||||
|
||||
|
||||
:cond_34
|
||||
add-int/2addr v4, v0
|
||||
goto :goto_3b
|
||||
|
||||
:cond_36
|
||||
const v6, 0xeed9
|
||||
div-int/2addr v3, v6
|
||||
nop
|
||||
|
||||
:goto_3b
|
||||
add-int/lit8 v5, v5, 0x1
|
||||
goto :goto_21
|
||||
|
||||
:cond_3e
|
||||
nop
|
||||
|
||||
:goto_3f
|
||||
nop
|
||||
# endless loop with empty body
|
||||
goto :goto_3f
|
||||
|
||||
:cond_41
|
||||
sget-object p1, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
invoke-static {v1, v2}, Ljava/lang/Double;->doubleToLongBits(D)J
|
||||
move-result-wide v0
|
||||
new-instance v2, Ljava/lang/StringBuilder;
|
||||
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
|
||||
const-string v5, "i21 d2 i22 = "
|
||||
invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
|
||||
const-string v3, ","
|
||||
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v2, v0, v1}, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v2, v4}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
|
||||
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
move-result-object v0
|
||||
invoke-virtual {p1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
return-void
|
||||
.end method
|
||||
Reference in New Issue
Block a user