[java-decompiler] IDEA-326015 Support pattern's exceptions

- add merge edges to graph

GitOrigin-RevId: 9a440c06c2bbd64975383520dbdc373ec9f87cb6
This commit is contained in:
Mikhail Pyltsin
2023-10-27 11:37:46 +02:00
committed by intellij-monorepo-bot
parent cf27e7505e
commit 87419d6672
4 changed files with 100 additions and 1 deletions

View File

@@ -21,6 +21,11 @@ public interface CodeConstants {
int BYTECODE_JAVA_15 = 59;
int BYTECODE_JAVA_16 = 60;
int BYTECODE_JAVA_17 = 61;
int BYTECODE_JAVA_18 = 62;
int BYTECODE_JAVA_19 = 63;
int BYTECODE_JAVA_20 = 64;
int BYTECODE_JAVA_21 = 65;
int BYTECODE_JAVA_22 = 66;
// ----------------------------------------------------------------------
// VARIABLE TYPES

View File

@@ -96,6 +96,8 @@ public class MethodProcessorRunnable implements Runnable {
DeadCodeHelper.removeGoTos(graph);
ExceptionDeobfuscator.duplicateMergedCatchBlocks(graph, cl);
ExceptionDeobfuscator.removeCircularRanges(graph);
ExceptionDeobfuscator.restorePopRanges(graph);

View File

@@ -1,6 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.modules.decompiler.deobfuscator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.code.Instruction;
import org.jetbrains.java.decompiler.code.InstructionSequence;
@@ -14,9 +15,12 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine;
import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;
import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;
import org.jetbrains.java.decompiler.struct.StructClass;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class ExceptionDeobfuscator {
@@ -65,7 +69,7 @@ public final class ExceptionDeobfuscator {
InstructionSequence seq = handler.getSeq();
Instruction firstinstr;
if (seq.length() > 0) {
if (!seq.isEmpty()) {
firstinstr = seq.getInstr(0);
if (firstinstr.opcode == CodeConstants.opc_pop ||
@@ -389,6 +393,84 @@ public final class ExceptionDeobfuscator {
return false;
}
/**
* Duplicates merged catch blocks; it needs to process files with record pattern matching,
* because the javac compiler collapses all catch blocks for same exception into one block.
* It breaks logic to find a possible way. It must be called before other optimizations; otherwise
* this decompiler will add a lot of empty blocks, which cannot be processed correctly.
*
* @param graph The control flow graph containing the merged catch blocks.
* @param cl The class containing the control flow graph.
*/
public static void duplicateMergedCatchBlocks(@NotNull ControlFlowGraph graph, @NotNull StructClass cl) {
if (!cl.hasRecordPatternSupport()) {
return;
}
Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();
for (ExceptionRangeCFG range : graph.getExceptions()) {
mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range);
}
for (Entry<BasicBlock, Set<ExceptionRangeCFG>> ent : mapRanges.entrySet()) {
BasicBlock handler = ent.getKey();
Set<ExceptionRangeCFG> ranges = ent.getValue();
if (ranges.size() == 1) {
continue;
}
if (handler.getLastInstruction().opcode != CodeConstants.opc_athrow) {
continue;
}
Set<String> exceptions = ranges.stream()
.map(t -> t.getExceptionTypes())
.flatMap(t -> t != null ? t.stream() : Stream.empty())
.collect(Collectors.toSet());
List<BasicBlock> successors = handler.getSuccessors();
if (successors != null && successors.size() == 1 && successors.get(0).getSuccessors().isEmpty() &&
successors.get(0).getSuccessorExceptions().isEmpty() &&
//exceptions contain only one type of exceptions, and it is not null, because null defines `finally` blocks
handler.getSuccessorExceptions().isEmpty() && exceptions.size() == 1 && !exceptions.contains(null)) {
for (ExceptionRangeCFG range : ranges) {
BasicBlock newHandler = handler.clone(++graph.last_id);
graph.getBlocks().addWithKey(newHandler, newHandler.id);
// only exception predecessors from this range considered
List<BasicBlock> lstPredExceptions = new ArrayList<>(handler.getPredecessorExceptions());
lstPredExceptions.retainAll(range.getProtectedRange());
// replace predecessors
for (BasicBlock pred : lstPredExceptions) {
ExceptionRangeCFG previousEdge = graph.getExceptionRange(handler, pred);
pred.replaceSuccessor(handler, newHandler);
if (previousEdge != null) {
previousEdge.setHandler(newHandler);
}
}
//add fast exit
newHandler.addSuccessor(successors.get(0));
for (BasicBlock successorException : handler.getSuccessorExceptions()) {
newHandler.addSuccessorException(successorException);
ExceptionRangeCFG previousEdge = graph.getExceptionRange(successorException, handler);
if (previousEdge != null) {
ArrayList<BasicBlock> newRanges = new ArrayList<>();
newRanges.add(newHandler);
ExceptionRangeCFG subRange = new ExceptionRangeCFG(newRanges, successorException, previousEdge.getExceptionTypes());
graph.getExceptions().add(subRange);
successorException.addPredecessorException(newHandler);
}
}
range.setHandler(newHandler);
}
for (BasicBlock successorException : handler.getSuccessorExceptions()) {
successorException.removePredecessorException(handler);
if (successorException.getPredecessorExceptions().isEmpty()) {
graph.removeBlock(successorException);
}
}
graph.removeBlock(handler);
}
}
}
public static void insertDummyExceptionHandlerBlocks(ControlFlowGraph graph, int bytecode_version) {
Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();
for (ExceptionRangeCFG range : graph.getExceptions()) {
@@ -419,7 +501,11 @@ public final class ExceptionDeobfuscator {
// replace predecessors
for (BasicBlock pred : lstPredExceptions) {
ExceptionRangeCFG previousEdge = graph.getExceptionRange(handler, pred);
pred.replaceSuccessor(handler, dummyBlock);
if (previousEdge != null) {
previousEdge.setHandler(dummyBlock);
}
}
// replace handler

View File

@@ -220,6 +220,9 @@ public class StructClass extends StructMember {
public boolean isVersion17() {
return majorVersion >= CodeConstants.BYTECODE_JAVA_17;
}
public boolean isVersion21() {
return majorVersion >= CodeConstants.BYTECODE_JAVA_21;
}
public boolean isPreviewVersion() {
return minorVersion == 0xFFFF;
@@ -235,6 +238,9 @@ public class StructClass extends StructMember {
public boolean hasEnhancedSwitchSupport() {
return isVersion14();
}
public boolean hasRecordPatternSupport() {
return isVersion21();
}
@Override
public String toString() {