IJ-CR-153375 [java-decompiler] IDEA-289211 set limits for some cases in decompiler

- change messages
- add test cases

GitOrigin-RevId: 8347f136818567abccf56a4c110b77456870fd4e
This commit is contained in:
Mikhail Pyltsin
2025-01-28 12:41:42 +00:00
committed by intellij-monorepo-bot
parent 9bc89d57d1
commit 0c6294eea6
5 changed files with 21 additions and 19 deletions
@@ -8,45 +8,41 @@ import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import static org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences.*;
import static org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences.MAX_DIRECT_NODES_COUNT;
import static org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences.MAX_DIRECT_VARIABLE_NODE_COUNT;
public class LimitContainer {
private final int maxDirectNodeCount;//-1 - don't check
@NotNull
private final AtomicLong directNodeCount = new AtomicLong();
@NotNull
private final String maxDirectNodeCountMessage;
private final int ssaConstructorSparseExRecordCount;//-1 - don't check
@NotNull
private final String ssaConstructorSparseExRecordCountMessage;
public LimitContainer(@NotNull Map<String, Object> properties) {
maxDirectNodeCount = (int)properties.getOrDefault(MAX_DIRECT_NODES_COUNT, -1);
maxDirectNodeCountMessage = (String)properties.getOrDefault(MAX_DIRECT_NODES_COUNT_MESSAGE, "Limits are exceeded");
ssaConstructorSparseExRecordCount = (int)properties.getOrDefault(MAX_DIRECT_VARIABLE_NODE_COUNT, -1);
ssaConstructorSparseExRecordCountMessage = (String)properties.getOrDefault(MAX_DIRECT_VARIABLE_NODES_COUNT_MESSAGE, "Limits are exceeded");
}
public void incrementAndCheckDirectNodeCount(@NotNull ControlFlowGraph graph) {
long newValue = directNodeCount.addAndGet(graph.getBlocks().size());
if (maxDirectNodeCount != -1 && newValue >= maxDirectNodeCount) {
throw new LimitExceededDecompilerException(maxDirectNodeCountMessage);
throw new LimitExceededDecompilerException(maxDirectNodeCount, newValue, "direct nodes");
}
}
public void checkSFormsFastMapDirect(@NotNull Map<String, SFormsFastMapDirect> inVarVersions,
@NotNull Map<String, SFormsFastMapDirect> outVarVersions) {
int newValue = inVarVersions.size() + outVarVersions.size();
if (ssaConstructorSparseExRecordCount != -1 &&
inVarVersions.size() + outVarVersions.size() > ssaConstructorSparseExRecordCount) {
throw new LimitExceededDecompilerException(ssaConstructorSparseExRecordCountMessage);
newValue > ssaConstructorSparseExRecordCount) {
throw new LimitExceededDecompilerException(ssaConstructorSparseExRecordCount, newValue, "variable nodes");
}
}
public static class LimitExceededDecompilerException extends RuntimeException {
public LimitExceededDecompilerException(String message) {
super(message);
public LimitExceededDecompilerException(long limit, long actualValue, String type) {
super("Limits for %s are exceeded. Current value: %s, limit: %s".formatted(type, actualValue, limit));
}
}
}
@@ -64,9 +64,7 @@ public interface IFernflowerPreferences {
String SKIP_EXTRA_FILES = "sef";
String MAX_DIRECT_NODES_COUNT = "mdnc";
String MAX_DIRECT_NODES_COUNT_MESSAGE = "mdncm";
String MAX_DIRECT_VARIABLE_NODE_COUNT = "mdvnc";
String MAX_DIRECT_VARIABLE_NODES_COUNT_MESSAGE = "mdvncm";
Map<String, String> DEFAULTS = getDefaults();
@@ -27,8 +27,6 @@ action.show.decompiled.name=Show Decompiled Class
decompiling.progress=Decompiling {0}
max.direct.nodes.count.message=Limits for direct nodes are exceeded, limits can be increased in Settings->Advanced settings->Java Bytecode Decompiler
max.variable.nodes.count.message=Limits for variable nodes are exceeded, limits can be increased in Settings->Advanced settings->Java Bytecode Decompiler
advanced.setting.decompiler.max.direct.nodes.count=Max count of direct nodes
advanced.setting.decompiler.max.variable.nodes.count=Max count of variable nodes
advanced.setting.group.decompiler=Java Bytecode Decompiler
@@ -131,10 +131,7 @@ class IdeaDecompiler : ClassFileDecompilers.Light() {
if (Registry.`is`("decompiler.dump.original.lines")) {
options[IFernflowerPreferences.DUMP_ORIGINAL_LINES] = "1"
}
options[IFernflowerPreferences.MAX_DIRECT_NODES_COUNT_MESSAGE] = IdeaDecompilerBundle.message("max.direct.nodes.count.message")
options[IFernflowerPreferences.MAX_DIRECT_NODES_COUNT] = AdvancedSettings.getInt("decompiler.max.direct.nodes.count")
options[IFernflowerPreferences.MAX_DIRECT_VARIABLE_NODES_COUNT_MESSAGE] = IdeaDecompilerBundle.message("max.variable.nodes.count.message")
options[IFernflowerPreferences.MAX_DIRECT_VARIABLE_NODE_COUNT] = AdvancedSettings.getInt("decompiler.max.variable.nodes.count")
val provider = MyBytecodeProvider(files)
@@ -80,6 +80,11 @@ class IdeaDecompilerTest : LightJavaCodeInsightFixtureTestCase() {
val file = getTestFile("${IdeaTestUtil.getMockJdk18Path().path}/jre/lib/rt.jar!/java/lang/String.class")
val decompiled = IdeaDecompiler().getText(file).toString()
assertTrue(decompiled, decompiled.contains("Limits for direct nodes are exceeded"))
//small methods are decompiled normally
assertTrue(decompiled, decompiled.contains("""
public String(char[] var1) {
this.value = Arrays.copyOf(var1, var1.length);
}""".trimIndent()))
}
finally {
AdvancedSettings.setInt(advancedSetting, previousCount)
@@ -94,6 +99,14 @@ class IdeaDecompilerTest : LightJavaCodeInsightFixtureTestCase() {
val file = getTestFile("${IdeaTestUtil.getMockJdk18Path().path}/jre/lib/rt.jar!/java/lang/String.class")
val decompiled = IdeaDecompiler().getText(file).toString()
assertTrue(decompiled, decompiled.contains("Limits for variable nodes are exceeded"))
//small methods are decompiled normally
assertTrue(decompiled, decompiled.contains(
"""
public char[] toCharArray() {
char[] var1 = new char[this.value.length];
System.arraycopy(this.value, 0, var1, 0, this.value.length);
return var1;
}""".trimIndent()))
}
finally {
AdvancedSettings.setInt(advancedSetting, previousCount)