feat(java-input): support StackMapTable to get stack info for unvisited jumps (#2271)

This commit is contained in:
Skylot
2024-09-14 22:33:28 +01:00
parent 7bb5c0a859
commit 5c83c22501
21 changed files with 620 additions and 131 deletions
@@ -71,7 +71,11 @@ public class CheckCode extends AbstractVisitor {
}
insnNode.getRegisterArgs(list);
for (RegisterArg arg : list) {
if (arg.getRegNum() >= regsCount) {
int regNum = arg.getRegNum();
if (regNum < 0) {
throw new JadxRuntimeException("Incorrect negative register number in instruction: " + insnNode);
}
if (regNum >= regsCount) {
throw new JadxRuntimeException("Incorrect register number in instruction: " + insnNode
+ ", expected to be less than " + regsCount);
}
@@ -90,7 +90,7 @@ public class BlockProcessor extends AbstractVisitor {
}
}
private static boolean deduplicateBlockInsns(BlockNode block) {
private static boolean deduplicateBlockInsns(MethodNode mth, BlockNode block) {
if (block.contains(AFlag.LOOP_START) || block.contains(AFlag.LOOP_END)) {
// search for same instruction at end of all predecessors blocks
List<BlockNode> predecessors = block.getPredecessors();
@@ -109,7 +109,7 @@ public class BlockProcessor extends AbstractVisitor {
List<InsnNode> insns = getLastInsns(predecessors.get(0), sameInsnCount);
insertAtStart(block, insns);
predecessors.forEach(pred -> getLastInsns(pred, sameInsnCount).clear());
LOG.debug("Move duplicate insns, count: {} to block {}", sameInsnCount, block);
mth.addDebugComment("Move duplicate insns, count: " + sameInsnCount + " to block " + block);
return true;
}
}
@@ -318,7 +318,7 @@ public class BlockProcessor extends AbstractVisitor {
boolean changed = false;
List<BlockNode> basicBlocks = mth.getBasicBlocks();
for (BlockNode basicBlock : basicBlocks) {
if (deduplicateBlockInsns(basicBlock)) {
if (deduplicateBlockInsns(mth, basicBlock)) {
changed = true;
}
}
@@ -0,0 +1,40 @@
package jadx.tests.integration.jbc;
import org.junit.jupiter.api.Test;
import jadx.tests.api.RaungTest;
import jadx.tests.api.extensions.profiles.TestProfile;
import jadx.tests.api.extensions.profiles.TestWithProfiles;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestStackConvert extends RaungTest {
@SuppressWarnings({ "UnnecessaryLocalVariable", "CallToPrintStackTrace", "printstacktrace" })
public static class TestCls {
public int parseIntDefault(String num, int defaultNum) {
try {
int defaultNum2 = Integer.parseInt(num);
return defaultNum2;
} catch (NumberFormatException e) {
System.out.println("Before println");
e.printStackTrace();
return defaultNum;
}
}
}
@TestWithProfiles(TestProfile.JAVA11)
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("Integer.parseInt(num)");
}
@Test
public void testRaung() {
assertThat(getClassNodeFromRaung())
.code()
.containsOne("Integer.parseInt(num)");
}
}
@@ -0,0 +1,53 @@
.version 52 # Java 8
.class public super jbc/TestStackConvert
.method public parseIntDefault(Ljava/lang/String;I)I
.max stack 3
.max locals 5
:L0
.local 0 "this" Ljbc/TestStackConvert;
.local 1 "num" Ljava/lang/String;
.local 2 "defaultNum" I
.line 13
aload 1
invokestatic java/lang/Integer parseInt (Ljava/lang/String;)I
:L1
.catch java/lang/NumberFormatException :L0 .. :L1 goto :L2
ireturn
:L3
.line 14
.stack full
stack 0 java/lang/NumberFormatException
local 0 jbc/TestStackConvert
local 1 java/lang/String
local 2 int
local 3 Top
local 4 java/lang/NumberFormatException
.end stack
astore 3
.local 3 "e" Ljava/lang/NumberFormatException;
.line 15
aload 3
invokevirtual java/lang/NumberFormatException printStackTrace ()V
.line 17
iload 2
.end local 3 # "e"
ireturn
:L2
.stack full
stack 0 java/lang/NumberFormatException
local 0 jbc/TestStackConvert
local 1 java/lang/String
local 2 int
.end stack
.end local 0 # "this"
.end local 1 # "num"
.end local 2 # "defaultNum"
astore 4
getstatic java/lang/System out Ljava/io/PrintStream;
ldc "Before println"
invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
aload 4
goto :L3
.end method