org.jetbrains.java.decompiler.modules.code.DeadCodeHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vineflower Show documentation
Show all versions of vineflower Show documentation
Modern Java & JVM language decompiler aiming to be as accurate as possible, with an emphasis on output quality.
The newest version!
// Copyright 2000-2020 JetBrains s.r.o. 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.code;
import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.util.DotExporter;
import java.util.*;
public final class DeadCodeHelper {
public static void removeDeadBlocks(ControlFlowGraph graph) {
Deque stack = new ArrayDeque<>();
HashSet setStacked = new HashSet<>();
stack.add(graph.getFirst());
setStacked.add(graph.getFirst());
while (!stack.isEmpty()) {
BasicBlock block = stack.removeFirst();
List lstSuccs = new ArrayList<>(block.getSuccs());
lstSuccs.addAll(block.getSuccExceptions());
for (BasicBlock succ : lstSuccs) {
if (!setStacked.contains(succ)) {
stack.add(succ);
setStacked.add(succ);
}
}
}
HashSet setAllBlocks = new HashSet<>(graph.getBlocks());
setAllBlocks.removeAll(setStacked);
for (BasicBlock block : setAllBlocks) {
graph.removeBlock(block);
}
}
public static void removeEmptyBlocks(ControlFlowGraph graph) {
List blocks = graph.getBlocks();
boolean cont;
do {
cont = false;
for (int i = blocks.size() - 1; i >= 0; i--) {
BasicBlock block = blocks.get(i);
if (removeEmptyBlock(graph, block, false)) {
cont = true;
break;
}
}
}
while (cont);
}
private static boolean removeEmptyBlock(ControlFlowGraph graph, BasicBlock block, boolean merging) {
boolean deletedRanges = false;
if (block.getSeq().isEmpty()) {
if (block.getSuccs().size() > 1) {
if (block.getPreds().size() > 1) {
// ambiguous block
throw new RuntimeException("ERROR: empty block with multiple predecessors and successors found");
}
else if (!merging) {
throw new RuntimeException("ERROR: empty block with multiple successors found");
}
}
HashSet setExits = new HashSet<>(graph.getLast().getPreds());
if (block.getPredExceptions().isEmpty() &&
(!setExits.contains(block) || block.getPreds().size() == 1)) {
if (setExits.contains(block)) {
BasicBlock pred = block.getPreds().get(0);
// FIXME: flag in the basic block
if (pred.getSuccs().size() != 1 || (!pred.getSeq().isEmpty()
&& pred.getSeq().getLastInstr().group == CodeConstants.GROUP_SWITCH)) {
return false;
}
}
HashSet setPreds = new HashSet<>(block.getPreds());
HashSet setSuccs = new HashSet<>(block.getSuccs());
// collect common exception ranges of predecessors and successors
HashSet setCommonExceptionHandlers = null;
for (int i = 0; i < 2; ++i) {
for (BasicBlock pred : i == 0 ? setPreds : setSuccs) {
if (setCommonExceptionHandlers == null) {
setCommonExceptionHandlers = new HashSet<>(pred.getSuccExceptions());
}
else {
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
}
}
}
// check the block to be in each of the common ranges
if (setCommonExceptionHandlers != null && !setCommonExceptionHandlers.isEmpty()) {
for (BasicBlock handler : setCommonExceptionHandlers) {
if (!block.getSuccExceptions().contains(handler)) {
return false;
}
}
}
// remove ranges consisting of this one block
List lstRanges = graph.getExceptions();
for (int i = lstRanges.size() - 1; i >= 0; i--) {
ExceptionRangeCFG range = lstRanges.get(i);
List lst = range.getProtectedRange();
if (lst.size() == 1 && lst.get(0) == block) {
if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {
block.removeSuccessorException(range.getHandler());
lstRanges.remove(i);
deletedRanges = true;
}
else {
return false;
}
}
}
// connect remaining nodes
if (merging) {
BasicBlock pred = block.getPreds().get(0);
pred.removeSuccessor(block);
List lstSuccs = new ArrayList<>(block.getSuccs());
for (BasicBlock succ : lstSuccs) {
block.removeSuccessor(succ);
pred.addSuccessor(succ);
}
}
else {
for (BasicBlock pred : setPreds) {
for (BasicBlock succ : setSuccs) {
pred.replaceSuccessor(block, succ);
}
}
}
// finally exit edges
Set setFinallyExits = graph.getFinallyExits();
if (setFinallyExits.contains(block)) {
setFinallyExits.remove(block);
setFinallyExits.add(setPreds.iterator().next());
}
// replace first if necessary
if (graph.getFirst() == block) {
if (setSuccs.size() != 1) {
throw new RuntimeException("multiple or no entry blocks!");
}
else {
graph.setFirst(setSuccs.iterator().next());
}
}
// remove this block
graph.removeBlock(block);
if (deletedRanges) {
removeDeadBlocks(graph);
}
}
}
return deletedRanges;
}
public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) {
HashSet marked = new HashSet<>();
if (block == dom) {
return true;
}
LinkedList lstNodes = new LinkedList<>();
lstNodes.add(block);
while (!lstNodes.isEmpty()) {
BasicBlock node = lstNodes.remove(0);
if (marked.contains(node)) {
continue;
}
else {
marked.add(node);
}
if (node == graph.getFirst()) {
return false;
}
for (int i = 0; i < node.getPreds().size(); i++) {
BasicBlock pred = node.getPreds().get(i);
if (pred != dom && !marked.contains(pred)) {
lstNodes.add(pred);
}
}
for (int i = 0; i < node.getPredExceptions().size(); i++) {
BasicBlock pred = node.getPredExceptions().get(i);
if (pred != dom && !marked.contains(pred)) {
lstNodes.add(pred);
}
}
}
return true;
}
public static void removeGotos(ControlFlowGraph graph) {
Set ignored = findCircularJumps(graph);
for (BasicBlock block : graph.getBlocks()) {
Instruction instr = block.getLastInstruction();
if (instr != null && instr.opcode == CodeConstants.opc_goto) {
// Part of an empty circular jump sequence. This needs to be condensed later, so we do not touch it
if (ignored.contains(block)) {
continue;
}
block.getSeq().removeLast();
}
}
removeEmptyBlocks(graph);
}
// Finds any circular jumps within the graph. Examples include:
//
// Single instruction infinte loop
// 0: goto 0;
//
// Multiple instruction infinite loop
// 0: goto 2;
// 1: goto 0;
// 2: goto 1;
//
private static Set findCircularJumps(ControlFlowGraph graph) {
Set ret = new HashSet<>();
// Temp list
Set blocks = new HashSet<>();
// Iterate through all blocks
for (BasicBlock block : graph.getBlocks()) {
if (ret.contains(block)) {
continue;
}
blocks.add(block);
// Jump traversal
BasicBlock check = block;
while (true) {
Instruction instr = check.getLastInstruction();
if (instr != null && instr.opcode == CodeConstants.opc_goto) {
if (check.getSuccs().size() == 1) {
check = check.getSuccs().get(0);
if (blocks.contains(check)) {
ret.addAll(blocks);
break; // Circular jump found
}
blocks.add(check);
} else {
break; // More than 1 successor, stop
}
} else {
break; // No goto, stop
}
}
// Clear temp list for next block
blocks.clear();
}
return ret;
}
public static void connectDummyExitBlock(ControlFlowGraph graph) {
BasicBlock exit = graph.getLast();
for (BasicBlock block : new HashSet<>(exit.getPreds())) {
exit.removePredecessor(block);
block.addSuccessor(exit);
}
}
public static void extendSynchronizedRangeToMonitorexit(ControlFlowGraph graph) {
while(true) {
boolean range_extended = false;
for (ExceptionRangeCFG range : graph.getExceptions()) {
Set setPreds = new HashSet<>();
for (BasicBlock block : range.getProtectedRange()) {
setPreds.addAll(block.getPreds());
}
for (BasicBlock basicBlock : range.getProtectedRange()) {
setPreds.remove(basicBlock);
}
if(setPreds.size() != 1) {
continue; // multiple predecessors, obfuscated range
}
BasicBlock predBlock = setPreds.iterator().next();
InstructionSequence predSeq = predBlock.getSeq();
if(predSeq.isEmpty() || predSeq.getLastInstr().opcode != CodeConstants.opc_monitorenter) {
continue; // not a synchronized range
}
boolean monitorexit_in_range = false;
Set setProtectedBlocks = new HashSet<>(range.getProtectedRange());
setProtectedBlocks.add(range.getHandler());
for (BasicBlock block : setProtectedBlocks) {
InstructionSequence blockSeq = block.getSeq();
for (int i = 0; i < blockSeq.length(); i++) {
if (blockSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
monitorexit_in_range = true;
break;
}
}
if(monitorexit_in_range) {
break;
}
}
if(monitorexit_in_range) {
continue; // protected range already contains monitorexit
}
Set setSuccs = new HashSet<>();
for (BasicBlock block : range.getProtectedRange()) {
setSuccs.addAll(block.getSuccs());
}
for (BasicBlock basicBlock : range.getProtectedRange()) {
setSuccs.remove(basicBlock);
}
if(setSuccs.size() != 1) {
continue; // non-unique successor
}
BasicBlock succBlock = setSuccs.iterator().next();
InstructionSequence succSeq = succBlock.getSeq();
int succ_monitorexit_index = -1;
for (int i = 0; i < succSeq.length(); i++) {
if (succSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
succ_monitorexit_index = i;
break;
}
}
if(succ_monitorexit_index < 0) {
continue; // monitorexit not found in the single successor block
}
BasicBlock handlerBlock = range.getHandler();
if(handlerBlock.getSuccs().size() != 1) {
continue; // non-unique handler successor
}
BasicBlock succHandler = handlerBlock.getSuccs().get(0);
InstructionSequence succHandlerSeq = succHandler.getSeq();
if(succHandlerSeq.isEmpty() || succHandlerSeq.getLastInstr().opcode != CodeConstants.opc_athrow) {
continue; // not a standard synchronized range
}
int handler_monitorexit_index = -1;
for (int i = 0; i < succHandlerSeq.length(); i++) {
if (succHandlerSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
handler_monitorexit_index = i;
break;
}
}
if(handler_monitorexit_index < 0) {
continue; // monitorexit not found in the handler successor block
}
// checks successful, prerequisites satisfied, now extend the range
// FIXME: what is this splitting doing, and why does it cause the loop to never finish?
// if(succ_monitorexit_index < succSeq.length() - 1) { // split block
//
// SimpleInstructionSequence seq = new SimpleInstructionSequence();
// for(int counter = 0; counter < succ_monitorexit_index; counter++) {
// seq.addInstruction(succSeq.getInstr(0), -1);
// succSeq.removeInstruction(0);
// }
//
// // build a separate block
// BasicBlock newblock = new BasicBlock(++graph.last_id);
// newblock.setSeq(seq);
//
// // insert new block
//
// for (BasicBlock block : new ArrayList<>(succBlock.getPreds())) {
// block.replaceSuccessor(succBlock, newblock);
// }
//
// newblock.addSuccessor(succBlock);
// graph.getBlocks().addWithKey(newblock, newblock.id);
//
// succBlock = newblock;
// }
// copy exception edges and extend protected ranges (successor block)
BasicBlock rangeExitBlock = succBlock.getPreds().get(0);
for (int j = 0; j < rangeExitBlock.getSuccExceptions().size(); j++) {
BasicBlock hd = rangeExitBlock.getSuccExceptions().get(j);
succBlock.addSuccessorException(hd);
ExceptionRangeCFG rng = graph.getExceptionRange(hd, rangeExitBlock);
rng.getProtectedRange().add(succBlock);
}
// copy instructions (handler successor block)
InstructionSequence handlerSeq = handlerBlock.getSeq();
for(int counter = 0; counter < handler_monitorexit_index; counter++) {
handlerSeq.addInstruction(succHandlerSeq.getInstr(0), -1);
succHandlerSeq.removeInstruction(0);
}
range_extended = true;
graph.addComment("$VF: Extended synchronized range to monitorexit");
break;
}
if(!range_extended) {
break;
}
}
// Extend range to monitorexit when the monitorexit block is a successor
for (ExceptionRangeCFG range : graph.getExceptions()) {
Set predecessors = new HashSet<>();
// Problems arise if we're too deeply nested- we don't handle that case yet!
boolean exceptionsOk = true;
for (BasicBlock block : range.getProtectedRange()) {
if (block.getSuccExceptions().size() > 1) {
exceptionsOk = false;
break;
}
}
if (!exceptionsOk) {
continue;
}
// Find all predecessors of the range
for (BasicBlock block : range.getProtectedRange()) {
predecessors.addAll(block.getPreds());
}
for (BasicBlock block : range.getProtectedRange()) {
predecessors.remove(block);
}
// Can't have more than one entrypoint!
if (predecessors.size() != 1) {
continue;
}
BasicBlock pred = predecessors.iterator().next();
if (pred.getSeq().isEmpty() || pred.getSeq().getLastInstr().opcode != CodeConstants.opc_monitorenter) {
continue; // not a synchronized range
}
Set successors = new HashSet<>();
for (BasicBlock block : range.getProtectedRange()) {
successors.addAll(block.getSuccs());
}
for (BasicBlock block : range.getProtectedRange()) {
successors.remove(block);
}
for (BasicBlock successor : successors) {
if (!successor.getSeq().isEmpty() && successor.getSeq().getLastInstr().opcode == CodeConstants.opc_monitorexit) {
// If the range doesn't have the monitorexit instruction, add it to the range
if (!range.getProtectedRange().contains(successor)) {
range.getProtectedRange().add(successor);
// Add exception edges to the range
successor.addSuccessorException(range.getHandler());
}
}
}
}
}
public static void incorporateValueReturns(ControlFlowGraph graph) {
for (BasicBlock block : graph.getBlocks()) {
InstructionSequence seq = block.getSeq();
int len = seq.length();
if (len > 0 && len < 3) {
boolean ok = false;
if (seq.getLastInstr().opcode >= CodeConstants.opc_ireturn && seq.getLastInstr().opcode <= CodeConstants.opc_return) {
if (len == 1) {
ok = true;
}
else if (seq.getLastInstr().opcode != CodeConstants.opc_return) {
switch (seq.getInstr(0).opcode) {
case CodeConstants.opc_iload:
case CodeConstants.opc_lload:
case CodeConstants.opc_fload:
case CodeConstants.opc_dload:
case CodeConstants.opc_aload:
case CodeConstants.opc_aconst_null:
case CodeConstants.opc_bipush:
case CodeConstants.opc_sipush:
case CodeConstants.opc_lconst_0:
case CodeConstants.opc_lconst_1:
case CodeConstants.opc_fconst_0:
case CodeConstants.opc_fconst_1:
case CodeConstants.opc_fconst_2:
case CodeConstants.opc_dconst_0:
case CodeConstants.opc_dconst_1:
case CodeConstants.opc_ldc:
case CodeConstants.opc_ldc_w:
case CodeConstants.opc_ldc2_w:
ok = true;
}
}
}
if (ok) {
if (!block.getPreds().isEmpty()) {
HashSet setPredHandlersUnion = new HashSet<>();
HashSet setPredHandlersIntersection = new HashSet<>();
boolean firstpred = true;
for (BasicBlock pred : block.getPreds()) {
if (firstpred) {
setPredHandlersIntersection.addAll(pred.getSuccExceptions());
firstpred = false;
}
else {
setPredHandlersIntersection.retainAll(pred.getSuccExceptions());
}
setPredHandlersUnion.addAll(pred.getSuccExceptions());
}
// add exception ranges from predecessors
for (BasicBlock basicBlock : block.getSuccExceptions()) {
setPredHandlersIntersection.remove(basicBlock);
}
BasicBlock predecessor = block.getPreds().get(0);
for (BasicBlock handler : setPredHandlersIntersection) {
ExceptionRangeCFG range = graph.getExceptionRange(handler, predecessor);
range.getProtectedRange().add(block);
block.addSuccessorException(handler);
}
// remove redundant ranges
HashSet setRangesToBeRemoved = new HashSet<>(block.getSuccExceptions());
setRangesToBeRemoved.removeAll(setPredHandlersUnion);
for (BasicBlock handler : setRangesToBeRemoved) {
ExceptionRangeCFG range = graph.getExceptionRange(handler, block);
if (range.getProtectedRange().size() > 1) {
range.getProtectedRange().remove(block);
block.removeSuccessorException(handler);
}
}
}
if (block.getPreds().size() == 1 && block.getPredExceptions().isEmpty()) {
BasicBlock bpred = block.getPreds().get(0);
if (bpred.getSuccs().size() == 1) {
// add exception ranges of predecessor
for (BasicBlock succ : bpred.getSuccExceptions()) {
if (!block.getSuccExceptions().contains(succ)) {
ExceptionRangeCFG range = graph.getExceptionRange(succ, bpred);
range.getProtectedRange().add(block);
block.addSuccessorException(succ);
}
}
// remove superfluous ranges from successors
for (BasicBlock succ : new HashSet<>(block.getSuccExceptions())) {
if (!bpred.getSuccExceptions().contains(succ)) {
ExceptionRangeCFG range = graph.getExceptionRange(succ, block);
if (range.getProtectedRange().size() > 1) {
range.getProtectedRange().remove(block);
block.removeSuccessorException(succ);
}
}
}
}
}
}
}
}
}
public static void mergeBasicBlocks(ControlFlowGraph graph) {
while (true) {
boolean merged = false;
int originBlocksCount = graph.getBlocks().size();
for (BasicBlock block : graph.getBlocks()) {
InstructionSequence seq = block.getSeq();
if (block.getSuccs().size() == 1) {
BasicBlock next = block.getSuccs().get(0);
if (next != graph.getLast() && (seq.isEmpty() || seq.getLastInstr().group != CodeConstants.GROUP_SWITCH)) {
if (next.getPreds().size() == 1 && next.getPredExceptions().isEmpty()
&& next != graph.getFirst()) {
// TODO: implement a dummy start block
boolean sameRanges = true;
for (ExceptionRangeCFG range : graph.getExceptions()) {
if (range.getProtectedRange().contains(block) ^
range.getProtectedRange().contains(next)) {
sameRanges = false;
break;
}
}
if (sameRanges) {
seq.addSequence(next.getSeq());
block.getInstrOldOffsets().addAll(next.getInstrOldOffsets());
next.getSeq().clear();
removeEmptyBlock(graph, next, true);
merged = true;
break;
}
}
}
}
}
if (!merged || graph.getBlocks().size() == originBlocksCount) {
break;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy