Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph Maven / Gradle / Ivy
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.code.cfg;
import org.jetbrains.java.decompiler.code.*;
import org.jetbrains.java.decompiler.code.interpreter.InstructionImpact;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
import org.jetbrains.java.decompiler.struct.gen.DataPoint;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.ListStack;
import org.jetbrains.java.decompiler.util.VBStyleCollection;
import java.util.*;
import java.util.Map.Entry;
public class ControlFlowGraph implements CodeConstants {
public int last_id = 0;
// *****************************************************************************
// private fields
// *****************************************************************************
private VBStyleCollection blocks;
private BasicBlock first;
private BasicBlock last;
private List exceptions;
private Map subroutines;
private Set finallyExits = new HashSet();
// *****************************************************************************
// constructors
// *****************************************************************************
public ControlFlowGraph(InstructionSequence seq) {
buildBlocks(seq);
}
// *****************************************************************************
// public methods
// *****************************************************************************
public void free() {
for (BasicBlock block : blocks) {
block.free();
}
blocks.clear();
first = null;
last = null;
exceptions.clear();
finallyExits.clear();
}
public void removeMarkers() {
for (BasicBlock block : blocks) {
block.mark = 0;
}
}
public String toString() {
if (blocks == null) return "Empty";
String new_line_separator = DecompilerContext.getNewLineSeparator();
StringBuilder buf = new StringBuilder();
for (BasicBlock block : blocks) {
buf.append("----- Block ").append(block.id).append(" -----").append(new_line_separator);
buf.append(block.toString());
buf.append("----- Edges -----").append(new_line_separator);
List suc = block.getSuccs();
for (int j = 0; j < suc.size(); j++) {
buf.append(">>>>>>>>(regular) Block ").append(suc.get(j).id).append(new_line_separator);
}
suc = block.getSuccExceptions();
for (int j = 0; j < suc.size(); j++) {
BasicBlock handler = suc.get(j);
ExceptionRangeCFG range = getExceptionRange(handler, block);
if (range == null) {
buf.append(">>>>>>>>(exception) Block ").append(handler.id).append("\t").append("ERROR: range not found!")
.append(new_line_separator);
}
else {
List exceptionTypes = range.getExceptionTypes();
if (exceptionTypes == null) {
buf.append(">>>>>>>>(exception) Block ").append(handler.id).append("\t").append("NULL").append(new_line_separator);
}
else {
for (String exceptionType : exceptionTypes) {
buf.append(">>>>>>>>(exception) Block ").append(handler.id).append("\t").append(exceptionType).append(new_line_separator);
}
}
}
}
buf.append("----- ----- -----").append(new_line_separator);
}
return buf.toString();
}
public void inlineJsr(StructMethod mt) {
processJsr();
removeJsr(mt);
removeMarkers();
DeadCodeHelper.removeEmptyBlocks(this);
}
public void removeBlock(BasicBlock block) {
while (block.getSuccs().size() > 0) {
block.removeSuccessor(block.getSuccs().get(0));
}
while (block.getSuccExceptions().size() > 0) {
block.removeSuccessorException(block.getSuccExceptions().get(0));
}
while (block.getPreds().size() > 0) {
block.getPreds().get(0).removeSuccessor(block);
}
while (block.getPredExceptions().size() > 0) {
block.getPredExceptions().get(0).removeSuccessorException(block);
}
last.removePredecessor(block);
blocks.removeWithKey(block.id);
for (int i = exceptions.size() - 1; i >= 0; i--) {
ExceptionRangeCFG range = exceptions.get(i);
if (range.getHandler() == block) {
exceptions.remove(i);
}
else {
List lstRange = range.getProtectedRange();
lstRange.remove(block);
if (lstRange.isEmpty()) {
exceptions.remove(i);
}
}
}
Iterator> it = subroutines.entrySet().iterator();
while (it.hasNext()) {
Entry ent = it.next();
if (ent.getKey() == block || ent.getValue() == block) {
it.remove();
}
}
}
public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) {
//List ranges = new ArrayList();
for (int i = exceptions.size() - 1; i >= 0; i--) {
ExceptionRangeCFG range = exceptions.get(i);
if (range.getHandler() == handler && range.getProtectedRange().contains(block)) {
return range;
//ranges.add(range);
}
}
return null;
//return ranges.isEmpty() ? null : ranges;
}
// public String getExceptionsUniqueString(BasicBlock handler, BasicBlock block) {
//
// List ranges = getExceptionRange(handler, block);
//
// if(ranges == null) {
// return null;
// } else {
// Set setExceptionStrings = new HashSet();
// for(ExceptionRangeCFG range : ranges) {
// setExceptionStrings.add(range.getExceptionType());
// }
//
// String ret = "";
// for(String exception : setExceptionStrings) {
// ret += exception;
// }
//
// return ret;
// }
// }
// *****************************************************************************
// private methods
// *****************************************************************************
private void buildBlocks(InstructionSequence instrseq) {
short[] states = findStartInstructions(instrseq);
Map mapInstrBlocks = new HashMap();
VBStyleCollection colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks);
blocks = colBlocks;
connectBlocks(colBlocks, mapInstrBlocks);
setExceptionEdges(instrseq, mapInstrBlocks);
setSubroutineEdges();
setFirstAndLastBlocks();
}
private static short[] findStartInstructions(InstructionSequence seq) {
int len = seq.length();
short[] inststates = new short[len];
Set excSet = new HashSet();
for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) {
excSet.add(handler.from_instr);
excSet.add(handler.to_instr);
excSet.add(handler.handler_instr);
}
for (int i = 0; i < len; i++) {
// exception blocks
if (excSet.contains(new Integer(i))) {
inststates[i] = 1;
}
Instruction instr = seq.getInstr(i);
switch (instr.group) {
case GROUP_JUMP:
inststates[((JumpInstruction)instr).destination] = 1;
case GROUP_RETURN:
if (i + 1 < len) {
inststates[i + 1] = 1;
}
break;
case GROUP_SWITCH:
SwitchInstruction swinstr = (SwitchInstruction)instr;
int[] dests = swinstr.getDestinations();
for (int j = dests.length - 1; j >= 0; j--) {
inststates[dests[j]] = 1;
}
inststates[swinstr.getDefaultdest()] = 1;
if (i + 1 < len) {
inststates[i + 1] = 1;
}
}
}
// first instruction
inststates[0] = 1;
return inststates;
}
private VBStyleCollection createBasicBlocks(short[] startblock,
InstructionSequence instrseq,
Map mapInstrBlocks) {
VBStyleCollection col = new VBStyleCollection();
InstructionSequence currseq = null;
List lstOffs = null;
int len = startblock.length;
short counter = 0;
int blockoffset = 0;
BasicBlock currentBlock = null;
for (int i = 0; i < len; i++) {
if (startblock[i] == 1) {
currentBlock = new BasicBlock(++counter);
currseq = currentBlock.getSeq();
lstOffs = currentBlock.getInstrOldOffsets();
col.addWithKey(currentBlock, currentBlock.id);
blockoffset = instrseq.getOffset(i);
}
startblock[i] = counter;
mapInstrBlocks.put(i, currentBlock);
currseq.addInstruction(instrseq.getInstr(i), instrseq.getOffset(i) - blockoffset);
lstOffs.add(instrseq.getOffset(i));
}
last_id = counter;
return col;
}
private static void connectBlocks(List lstbb, Map mapInstrBlocks) {
for (int i = 0; i < lstbb.size(); i++) {
BasicBlock block = lstbb.get(i);
Instruction instr = block.getLastInstruction();
boolean fallthrough = instr.canFallthrough();
BasicBlock bTemp;
switch (instr.group) {
case GROUP_JUMP:
int dest = ((JumpInstruction)instr).destination;
bTemp = mapInstrBlocks.get(dest);
block.addSuccessor(bTemp);
break;
case GROUP_SWITCH:
SwitchInstruction sinstr = (SwitchInstruction)instr;
int[] dests = sinstr.getDestinations();
bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultdest());
block.addSuccessor(bTemp);
for (int j = 0; j < dests.length; j++) {
bTemp = mapInstrBlocks.get(dests[j]);
block.addSuccessor(bTemp);
}
}
if (fallthrough && i < lstbb.size() - 1) {
BasicBlock defaultBlock = lstbb.get(i + 1);
block.addSuccessor(defaultBlock);
}
}
}
private void setExceptionEdges(InstructionSequence instrseq, Map instrBlocks) {
exceptions = new ArrayList();
Map mapRanges = new HashMap();
for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) {
BasicBlock from = instrBlocks.get(handler.from_instr);
BasicBlock to = instrBlocks.get(handler.to_instr);
BasicBlock handle = instrBlocks.get(handler.handler_instr);
String key = from.id + ":" + to.id + ":" + handle.id;
if (mapRanges.containsKey(key)) {
ExceptionRangeCFG range = mapRanges.get(key);
range.addExceptionType(handler.exceptionClass);
}
else {
List protectedRange = new ArrayList();
for (int j = from.id; j < to.id; j++) {
BasicBlock block = blocks.getWithKey(j);
protectedRange.add(block);
block.addSuccessorException(handle);
}
ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null
? null
: Collections.singletonList(handler.exceptionClass));
mapRanges.put(key, range);
exceptions.add(range);
}
}
}
private void setSubroutineEdges() {
final Map subroutines = new HashMap();
for (BasicBlock block : blocks) {
if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) {
LinkedList stack = new LinkedList();
LinkedList> stackJsrStacks = new LinkedList>();
Set setVisited = new HashSet();
stack.add(block);
stackJsrStacks.add(new LinkedList());
while (!stack.isEmpty()) {
BasicBlock node = stack.removeFirst();
LinkedList jsrstack = stackJsrStacks.removeFirst();
setVisited.add(node);
switch (node.getSeq().getLastInstr().opcode) {
case CodeConstants.opc_jsr:
jsrstack.add(node);
break;
case CodeConstants.opc_ret:
BasicBlock enter = jsrstack.getLast();
BasicBlock exit = blocks.getWithKey(enter.id + 1); // FIXME: find successor in a better way
if (exit != null) {
if (!node.isSuccessor(exit)) {
node.addSuccessor(exit);
}
jsrstack.removeLast();
subroutines.put(enter, exit);
}
else {
throw new RuntimeException("ERROR: last instruction jsr");
}
}
if (!jsrstack.isEmpty()) {
for (BasicBlock succ : node.getSuccs()) {
if (!setVisited.contains(succ)) {
stack.add(succ);
stackJsrStacks.add(new LinkedList(jsrstack));
}
}
}
}
}
}
this.subroutines = subroutines;
}
private void processJsr() {
while (true) {
if (processJsrRanges() == 0) break;
}
}
private static class JsrRecord {
private final BasicBlock jsr;
private final Set range;
private final BasicBlock ret;
private JsrRecord(BasicBlock jsr, Set range, BasicBlock ret) {
this.jsr = jsr;
this.range = range;
this.ret = ret;
}
}
private int processJsrRanges() {
List lstJsrAll = new ArrayList();
// get all jsr ranges
for (Entry ent : subroutines.entrySet()) {
BasicBlock jsr = ent.getKey();
BasicBlock ret = ent.getValue();
lstJsrAll.add(new JsrRecord(jsr, getJsrRange(jsr, ret), ret));
}
// sort ranges
// FIXME: better sort order
List lstJsr = new ArrayList();
for (JsrRecord arr : lstJsrAll) {
int i = 0;
for (; i < lstJsr.size(); i++) {
JsrRecord arrJsr = lstJsr.get(i);
if (arrJsr.range.contains(arr.jsr)) {
break;
}
}
lstJsr.add(i, arr);
}
// find the first intersection
for (int i = 0; i < lstJsr.size(); i++) {
JsrRecord arr = lstJsr.get(i);
Set set = arr.range;
for (int j = i + 1; j < lstJsr.size(); j++) {
JsrRecord arr1 = lstJsr.get(j);
Set set1 = arr1.range;
if (!set.contains(arr1.jsr) && !set1.contains(arr.jsr)) { // rang 0 doesn't contain entry 1 and vice versa
Set setc = new HashSet(set);
setc.retainAll(set1);
if (!setc.isEmpty()) {
splitJsrRange(arr.jsr, arr.ret, setc);
return 1;
}
}
}
}
return 0;
}
private Set getJsrRange(BasicBlock jsr, BasicBlock ret) {
Set blocks = new HashSet();
List lstNodes = new LinkedList();
lstNodes.add(jsr);
BasicBlock dom = jsr.getSuccs().get(0);
while (!lstNodes.isEmpty()) {
BasicBlock node = lstNodes.remove(0);
for (int j = 0; j < 2; j++) {
List lst;
if (j == 0) {
if (node.getLastInstruction().opcode == CodeConstants.opc_ret) {
if (node.getSuccs().contains(ret)) {
continue;
}
}
lst = node.getSuccs();
}
else {
if (node == jsr) {
continue;
}
lst = node.getSuccExceptions();
}
CHILD:
for (int i = lst.size() - 1; i >= 0; i--) {
BasicBlock child = lst.get(i);
if (!blocks.contains(child)) {
if (node != jsr) {
for (int k = 0; k < child.getPreds().size(); k++) {
if (!DeadCodeHelper.isDominator(this, child.getPreds().get(k), dom)) {
continue CHILD;
}
}
for (int k = 0; k < child.getPredExceptions().size(); k++) {
if (!DeadCodeHelper.isDominator(this, child.getPredExceptions().get(k), dom)) {
continue CHILD;
}
}
}
// last block is a dummy one
if (child != last) {
blocks.add(child);
}
lstNodes.add(child);
}
}
}
}
return blocks;
}
private void splitJsrRange(BasicBlock jsr, BasicBlock ret, Set common_blocks) {
List lstNodes = new LinkedList();
Map mapNewNodes = new HashMap();
lstNodes.add(jsr);
mapNewNodes.put(jsr.id, jsr);
while (!lstNodes.isEmpty()) {
BasicBlock node = lstNodes.remove(0);
for (int j = 0; j < 2; j++) {
List lst;
if (j == 0) {
if (node.getLastInstruction().opcode == CodeConstants.opc_ret) {
if (node.getSuccs().contains(ret)) {
continue;
}
}
lst = node.getSuccs();
}
else {
if (node == jsr) {
continue;
}
lst = node.getSuccExceptions();
}
for (int i = lst.size() - 1; i >= 0; i--) {
BasicBlock child = lst.get(i);
Integer childid = child.id;
if (mapNewNodes.containsKey(childid)) {
node.replaceSuccessor(child, mapNewNodes.get(childid));
}
else if (common_blocks.contains(child)) {
// make a copy of the current block
BasicBlock copy = (BasicBlock)child.clone();
copy.id = ++last_id;
// copy all successors
if (copy.getLastInstruction().opcode == CodeConstants.opc_ret &&
child.getSuccs().contains(ret)) {
copy.addSuccessor(ret);
child.removeSuccessor(ret);
}
else {
for (int k = 0; k < child.getSuccs().size(); k++) {
copy.addSuccessor(child.getSuccs().get(k));
}
}
for (int k = 0; k < child.getSuccExceptions().size(); k++) {
copy.addSuccessorException(child.getSuccExceptions().get(k));
}
lstNodes.add(copy);
mapNewNodes.put(childid, copy);
if (last.getPreds().contains(child)) {
last.addPredecessor(copy);
}
node.replaceSuccessor(child, copy);
blocks.addWithKey(copy, copy.id);
}
else {
// stop at the first fixed node
//lstNodes.add(child);
mapNewNodes.put(childid, child);
}
}
}
}
// note: subroutines won't be copied!
splitJsrExceptionRanges(common_blocks, mapNewNodes);
}
private void splitJsrExceptionRanges(Set common_blocks, Map mapNewNodes) {
for (int i = exceptions.size() - 1; i >= 0; i--) {
ExceptionRangeCFG range = exceptions.get(i);
List lstRange = range.getProtectedRange();
HashSet setBoth = new HashSet(common_blocks);
setBoth.retainAll(lstRange);
if (setBoth.size() > 0) {
List lstNewRange;
if (setBoth.size() == lstRange.size()) {
lstNewRange = new ArrayList();
ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange,
mapNewNodes.get(range.getHandler().id), range.getExceptionTypes());
exceptions.add(newRange);
}
else {
lstNewRange = lstRange;
}
for (BasicBlock block : setBoth) {
lstNewRange.add(mapNewNodes.get(block.id));
}
}
}
}
private void removeJsr(StructMethod mt) {
removeJsrInstructions(mt.getClassStruct().getPool(), first, DataPoint.getInitialDataPoint(mt));
}
private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) {
ListStack stack = data.getStack();
InstructionSequence seq = block.getSeq();
for (int i = 0; i < seq.length(); i++) {
Instruction instr = seq.getInstr(i);
VarType var = null;
if (instr.opcode == CodeConstants.opc_astore || instr.opcode == CodeConstants.opc_pop) {
var = stack.getByOffset(-1);
}
InstructionImpact.stepTypes(data, instr, pool);
switch (instr.opcode) {
case CodeConstants.opc_jsr:
case CodeConstants.opc_ret:
seq.removeInstruction(i);
i--;
break;
case CodeConstants.opc_astore:
case CodeConstants.opc_pop:
if (var.type == CodeConstants.TYPE_ADDRESS) {
seq.removeInstruction(i);
i--;
}
}
}
block.mark = 1;
for (int i = 0; i < block.getSuccs().size(); i++) {
BasicBlock suc = block.getSuccs().get(i);
if (suc.mark != 1) {
removeJsrInstructions(pool, suc, data.copy());
}
}
for (int i = 0; i < block.getSuccExceptions().size(); i++) {
BasicBlock suc = block.getSuccExceptions().get(i);
if (suc.mark != 1) {
DataPoint point = new DataPoint();
point.setLocalVariables(new ArrayList(data.getLocalVariables()));
point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null));
removeJsrInstructions(pool, suc, point);
}
}
}
private void setFirstAndLastBlocks() {
first = blocks.get(0);
last = new BasicBlock(++last_id);
for (BasicBlock block : blocks) {
if (block.getSuccs().isEmpty()) {
last.addPredecessor(block);
}
}
}
public List getReversePostOrder() {
List res = new LinkedList();
addToReversePostOrderListIterative(first, res);
return res;
}
private static void addToReversePostOrderListIterative(BasicBlock root, List lst) {
LinkedList stackNode = new LinkedList();
LinkedList stackIndex = new LinkedList();
Set setVisited = new HashSet();
stackNode.add(root);
stackIndex.add(0);
while (!stackNode.isEmpty()) {
BasicBlock node = stackNode.getLast();
int index = stackIndex.removeLast();
setVisited.add(node);
List lstSuccs = new ArrayList(node.getSuccs());
lstSuccs.addAll(node.getSuccExceptions());
for (; index < lstSuccs.size(); index++) {
BasicBlock succ = lstSuccs.get(index);
if (!setVisited.contains(succ)) {
stackIndex.add(index + 1);
stackNode.add(succ);
stackIndex.add(0);
break;
}
}
if (index == lstSuccs.size()) {
lst.add(0, node);
stackNode.removeLast();
}
}
}
// *****************************************************************************
// getter and setter methods
// *****************************************************************************
public VBStyleCollection getBlocks() {
return blocks;
}
public void setBlocks(VBStyleCollection blocks) {
this.blocks = blocks;
}
public BasicBlock getFirst() {
return first;
}
public void setFirst(BasicBlock first) {
this.first = first;
}
public List getEndBlocks() {
return last.getPreds();
}
public List getExceptions() {
return exceptions;
}
public void setExceptions(List exceptions) {
this.exceptions = exceptions;
}
public BasicBlock getLast() {
return last;
}
public void setLast(BasicBlock last) {
this.last = last;
}
public Map getSubroutines() {
return subroutines;
}
public void setSubroutines(Map subroutines) {
this.subroutines = subroutines;
}
public Set getFinallyExits() {
return finallyExits;
}
public void setFinallyExits(HashSet finallyExits) {
this.finallyExits = finallyExits;
}
}