Files
fernflower/src/de/fernflower/modules/decompiler/FinallyProcessor.java
Stiver 96379678e6 - method attribute 'default' (Java 8)
- instruction 'invokedynamic' (Java 7)
- bugfixing
2014-03-13 23:02:30 +01:00

1032 lines
31 KiB
Java

/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
*
* (C) 2008 - 2010, Stiver
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
*/
package de.fernflower.modules.decompiler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import de.fernflower.code.CodeConstants;
import de.fernflower.code.ConstantsUtil;
import de.fernflower.code.Instruction;
import de.fernflower.code.InstructionSequence;
import de.fernflower.code.SimpleInstructionSequence;
import de.fernflower.code.cfg.BasicBlock;
import de.fernflower.code.cfg.ControlFlowGraph;
import de.fernflower.code.cfg.ExceptionRangeCFG;
import de.fernflower.main.DecompilerContext;
import de.fernflower.main.collectors.CounterContainer;
import de.fernflower.main.extern.IFernflowerPreferences;
import de.fernflower.modules.code.DeadCodeHelper;
import de.fernflower.modules.decompiler.exps.AssignmentExprent;
import de.fernflower.modules.decompiler.exps.ExitExprent;
import de.fernflower.modules.decompiler.exps.Exprent;
import de.fernflower.modules.decompiler.exps.VarExprent;
import de.fernflower.modules.decompiler.sforms.DirectGraph;
import de.fernflower.modules.decompiler.sforms.DirectNode;
import de.fernflower.modules.decompiler.sforms.FlattenStatementsHelper;
import de.fernflower.modules.decompiler.sforms.SSAConstructorSparseEx;
import de.fernflower.modules.decompiler.stats.BasicBlockStatement;
import de.fernflower.modules.decompiler.stats.CatchAllStatement;
import de.fernflower.modules.decompiler.stats.RootStatement;
import de.fernflower.modules.decompiler.stats.Statement;
import de.fernflower.modules.decompiler.vars.VarProcessor;
import de.fernflower.modules.decompiler.vars.VarVersionPaar;
import de.fernflower.struct.StructMethod;
import de.fernflower.struct.gen.VarType;
import de.fernflower.util.InterpreterUtil;
public class FinallyProcessor {
private HashMap<Integer, Integer> finallyBlockIDs = new HashMap<Integer, Integer>();
private HashMap<Integer, Integer> catchallBlockIDs = new HashMap<Integer, Integer>();
private VarProcessor varprocessor;
public FinallyProcessor(VarProcessor varprocessor) {
this.varprocessor = varprocessor;
}
public boolean iterateGraph(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
// return processStatement(mt, root, graph, root);
return processStatementEx(mt, root, graph);
}
private boolean processStatementEx(StructMethod mt, RootStatement root, ControlFlowGraph graph) {
int bytecode_version = mt.getClassStruct().getBytecodeVersion();
LinkedList<Statement> stack = new LinkedList<Statement>();
stack.add(root);
while(!stack.isEmpty()) {
Statement stat = stack.removeLast();
Statement parent = stat.getParent();
if(parent != null && parent.type == Statement.TYPE_CATCHALL &&
stat == parent.getFirst() && !parent.isCopied()) {
CatchAllStatement fin = (CatchAllStatement)parent;
BasicBlock head = fin.getBasichead().getBlock();
BasicBlock handler = fin.getHandler().getBasichead().getBlock();
if(catchallBlockIDs.containsKey(handler.id)) {
; // do nothing
}else if(finallyBlockIDs.containsKey(handler.id)) {
fin.setFinally(true);
Integer var = finallyBlockIDs.get(handler.id);
fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor));
} else {
Object[] inf = getFinallyInformation(mt, root, fin);
if(inf == null) { // inconsistent finally
catchallBlockIDs.put(handler.id, null);
} else {
if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
finallyBlockIDs.put(handler.id, null);
} else {
int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf, bytecode_version);
finallyBlockIDs.put(handler.id, varindex);
}
DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally
DeadCodeHelper.removeEmptyBlocks(graph);
DeadCodeHelper.mergeBasicBlocks(graph);
}
return true;
}
}
stack.addAll(stat.getStats());
}
return false;
}
// private boolean processStatement(StructMethod mt, RootStatement root, ControlFlowGraph graph, Statement stat) {
//
// boolean res = false;
//
// for(int i=stat.getStats().size()-1;i>=0;i--) {
// if(processStatement(mt, root, graph, stat.getStats().get(i))) {
// return true;
// }
// }
//
//
// if(stat.type == Statement.TYPE_CATCHALL && !stat.isCopied()) {
//
// CatchAllStatement fin = (CatchAllStatement)stat;
// BasicBlock head = fin.getBasichead().getBlock();
// BasicBlock handler = fin.getHandler().getBasichead().getBlock();
//
// if(catchallBlockIDs.containsKey(handler.id)) {
// ; // do nothing
// }else if(finallyBlockIDs.containsKey(handler.id)) {
//
// fin.setFinally(true);
//
// Integer var = finallyBlockIDs.get(handler.id);
// fin.setMonitor(var==null?null:new VarExprent(var.intValue(), VarType.VARTYPE_INT, varprocessor));
//
// } else {
//
// Object[] inf = getFinallyInformation(mt, root, fin);
//
// if(inf == null) { // inconsistent finally
// catchallBlockIDs.put(handler.id, null);
// } else {
//
// if(DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
// finallyBlockIDs.put(handler.id, null);
// } else {
//
// int varindex = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
// insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varindex, inf);
//
// finallyBlockIDs.put(handler.id, varindex);
// }
//
// DeadCodeHelper.removeEmptyBlocks(graph);
// DeadCodeHelper.mergeBasicBlocks(graph);
// }
//
// res = true;
// }
// }
//
// return res;
// }
private Object[] getFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) {
HashMap<BasicBlock, Boolean> mapLast = new HashMap<BasicBlock, Boolean>();
BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
BasicBlock firstBasicBlock = firstBlockStatement.getBlock();
Instruction instrFirst = firstBasicBlock.getInstruction(0);
int firstcode = 0;
switch(instrFirst.opcode) {
case CodeConstants.opc_pop:
firstcode = 1;
break;
case CodeConstants.opc_astore:
firstcode = 2;
}
ExprProcessor proc = new ExprProcessor();
proc.processStatement(root, mt.getClassStruct().getPool());
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt);
List<Exprent> lstExprents = firstBlockStatement.getExprents();
VarVersionPaar varpaar = new VarVersionPaar((VarExprent)((AssignmentExprent)lstExprents.get(firstcode==2?1:0)).getLeft());
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
DirectGraph dgraph = flatthelper.buildDirectGraph(root);
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
stack.add(dgraph.first);
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
while(!stack.isEmpty()) {
DirectNode node = stack.removeFirst();
if(setVisited.contains(node)) {
continue;
}
setVisited.add(node);
BasicBlockStatement blockStatement = null;
if(node.block != null) {
blockStatement = node.block;
} else if(node.preds.size() == 1) {
blockStatement = node.preds.get(0).block;
}
boolean isTrueExit = true;
if(firstcode != 1) {
isTrueExit = false;
for(int i=0;i<node.exprents.size();i++) {
Exprent exprent = node.exprents.get(i);
if(firstcode == 0) {
List<Exprent> lst = exprent.getAllExprents();
lst.add(exprent);
boolean found = false;
for(Exprent expr : lst) {
if(expr.type == Exprent.EXPRENT_VAR && new VarVersionPaar((VarExprent)expr).equals(varpaar)) {
found = true;
break;
}
}
if(found) {
found = false;
if(exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent;
if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) {
found = true;
}
}
if(!found) {
return null;
} else {
isTrueExit = true;
}
}
} else if(firstcode == 2) {
// search for a load instruction
if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent assexpr = (AssignmentExprent)exprent;
if(assexpr.getRight().type == Exprent.EXPRENT_VAR &&
new VarVersionPaar((VarExprent)assexpr.getRight()).equals(varpaar)) {
Exprent next = null;
if(i == node.exprents.size()-1) {
if(node.succs.size() == 1) {
DirectNode nd = node.succs.get(0);
if(!nd.exprents.isEmpty()) {
next = nd.exprents.get(0);
}
}
} else {
next = node.exprents.get(i+1);
}
boolean found = false;
if(next != null && next.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)next;
if(exexpr.getExittype() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR
&& assexpr.getLeft().equals(exexpr.getValue())) {
found = true;
}
}
if(!found) {
return null;
} else {
isTrueExit = true;
}
}
}
}
}
}
// find finally exits
if(blockStatement != null && blockStatement.getBlock() != null) {
Statement handler = fstat.getHandler();
for(StatEdge edge : blockStatement.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
if(edge.getType() != StatEdge.TYPE_REGULAR && handler.containsStatement(blockStatement)
&& !handler.containsStatement(edge.getDestination())) {
Boolean existingFlag = mapLast.get(blockStatement.getBlock());
// note: the dummy node is also processed!
if(existingFlag == null || !existingFlag) {
mapLast.put(blockStatement.getBlock(), isTrueExit);
break;
}
}
}
}
stack.addAll(node.succs);
}
// empty finally block?
if(fstat.getHandler().type == Statement.TYPE_BASICBLOCK) {
boolean isEmpty = false;
boolean isFirstLast = mapLast.containsKey(firstBasicBlock);
InstructionSequence seq = firstBasicBlock.getSeq();
switch(firstcode) {
case 0:
isEmpty = isFirstLast && seq.length() == 1;
break;
case 1:
isEmpty = seq.length() == 1;
break;
case 2:
isEmpty = isFirstLast?seq.length()==3:seq.length()==1;
}
if(isEmpty) {
firstcode = 3;
}
}
return new Object[] {firstcode, mapLast};
}
private void insertSemaphore(ControlFlowGraph graph, HashSet<BasicBlock> setTry, BasicBlock head, BasicBlock handler, int var, Object[] information, int bytecode_version) {
HashSet<BasicBlock> setCopy = new HashSet<BasicBlock>(setTry);
int finallytype = (Integer)information[0];
HashMap<BasicBlock, Boolean> mapLast = (HashMap<BasicBlock, Boolean>)information[1];
// first and last statements
removeExceptionInstructionsEx(handler, 1, finallytype);
for(Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {
BasicBlock last = entry.getKey();
if(entry.getValue()) {
removeExceptionInstructionsEx(last, 2, finallytype);
graph.getFinallyExits().add(last);
}
}
// disable semaphore at statement exit points
for(BasicBlock block: setTry) {
List<BasicBlock> lstSucc = block.getSuccs();
for(BasicBlock dest : lstSucc) {
// break out
if(!setCopy.contains(dest) && dest != graph.getLast()) {
// disable semaphore
SimpleInstructionSequence seq = new SimpleInstructionSequence();
seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}) , -1);
seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1);
// build a separate block
BasicBlock newblock = new BasicBlock(++graph.last_id);
newblock.setSeq(seq);
// insert between block and dest
block.replaceSuccessor(dest, newblock);
newblock.addSuccessor(dest);
setCopy.add(newblock);
graph.getBlocks().addWithKey(newblock, newblock.id);
// exception ranges
// FIXME: special case synchronized
// copy exception edges and extend protected ranges
for(int j=0;j<block.getSuccExceptions().size();j++) {
BasicBlock hd = block.getSuccExceptions().get(j);
newblock.addSuccessorException(hd);
ExceptionRangeCFG range = graph.getExceptionRange(hd, block);
range.getProtectedRange().add(newblock);
}
}
}
}
// enable semaphor at the statement entrance
SimpleInstructionSequence seq = new SimpleInstructionSequence();
seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}) , -1);
seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1);
BasicBlock newhead = new BasicBlock(++graph.last_id);
newhead.setSeq(seq);
insertBlockBefore(graph, head, newhead);
// initialize semaphor with false
seq = new SimpleInstructionSequence();
seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}) , -1);
seq.addInstruction(ConstantsUtil.getInstructionInstance(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}) , -1);
BasicBlock newheadinit = new BasicBlock(++graph.last_id);
newheadinit.setSeq(seq);
insertBlockBefore(graph, newhead, newheadinit);
setCopy.add(newhead);
setCopy.add(newheadinit);
for(BasicBlock hd: new HashSet<BasicBlock>(newheadinit.getSuccExceptions())) {
ExceptionRangeCFG range = graph.getExceptionRange(hd, newheadinit);
if(setCopy.containsAll(range.getProtectedRange())) {
newheadinit.removeSuccessorException(hd);
range.getProtectedRange().remove(newheadinit);
}
}
return;
}
private void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) {
List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
lstTemp.addAll(oldblock.getPreds());
lstTemp.addAll(oldblock.getPredExceptions());
// replace predecessors
for(BasicBlock pred: lstTemp) {
pred.replaceSuccessor(oldblock, newblock);
}
// copy exception edges and extend protected ranges
for(BasicBlock hd: oldblock.getSuccExceptions()) {
newblock.addSuccessorException(hd);
ExceptionRangeCFG range = graph.getExceptionRange(hd, oldblock);
range.getProtectedRange().add(newblock);
}
// replace handler
for(ExceptionRangeCFG range: graph.getExceptions()) {
if(range.getHandler() == oldblock) {
range.setHandler(newblock);
}
}
newblock.addSuccessor(oldblock);
graph.getBlocks().addWithKey(newblock, newblock.id);
if(graph.getFirst() == oldblock) {
graph.setFirst(newblock);
}
}
private HashSet<BasicBlock> getAllBasicBlocks(Statement stat) {
List<Statement> lst = new LinkedList<Statement>();
lst.add(stat);
int index = 0;
do {
Statement st = lst.get(index);
if(st.type == Statement.TYPE_BASICBLOCK) {
index++;
} else {
lst.addAll(st.getStats());
lst.remove(index);
}
} while(index<lst.size());
HashSet<BasicBlock> res = new HashSet<BasicBlock>();
for(Statement st: lst) {
res.add(((BasicBlockStatement)st).getBlock());
}
return res;
}
private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Object[] information) {
HashSet<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());
HashSet<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
int finallytype = (Integer)information[0];
HashMap<BasicBlock, Boolean> mapLast = (HashMap<BasicBlock, Boolean>)information[1];
BasicBlock first = fstat.getHandler().getBasichead().getBlock();
boolean skippedFirst = false;
if(finallytype == 3) {
// empty finally
removeExceptionInstructionsEx(first, 3, finallytype);
if(mapLast.containsKey(first)) {
graph.getFinallyExits().add(first);
}
return true;
} else {
if(first.getSeq().length() == 1 && finallytype>0) {
BasicBlock firstsuc = first.getSuccs().get(0);
if(catchBlocks.contains(firstsuc)) {
first = firstsuc;
skippedFirst = true;
}
}
}
// identify start blocks
HashSet<BasicBlock> startBlocks = new HashSet<BasicBlock>();
for(BasicBlock block: tryBlocks) {
startBlocks.addAll(block.getSuccs());
}
// throw in the try body will point directly to the dummy exit
// so remove dummy exit
startBlocks.remove(graph.getLast());
startBlocks.removeAll(tryBlocks);
List<Object[]> lstAreas = new ArrayList<Object[]>();
for(BasicBlock start: startBlocks) {
Object[] arr = compareSubgraphsEx(graph, start, catchBlocks, first, finallytype, mapLast, skippedFirst);
if(arr == null) {
return false;
}
lstAreas.add(new Object[] {start, arr[0], arr[1]});
}
// try {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
// } catch(Exception ex){ex.printStackTrace();}
// delete areas
for(Object[] area: lstAreas) {
deleteArea(graph, area);
}
// try {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
// } catch(Exception ex){ex.printStackTrace();}
// INFO: empty basic blocks may remain in the graph!
for(Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {
BasicBlock last = entry.getKey();
if(entry.getValue()) {
removeExceptionInstructionsEx(last, 2, finallytype);
graph.getFinallyExits().add(last);
}
}
removeExceptionInstructionsEx(fstat.getHandler().getBasichead().getBlock(), 1, finallytype);
return true;
}
private Object[] compareSubgraphsEx(ControlFlowGraph graph, BasicBlock startSample, HashSet<BasicBlock> catchBlocks, BasicBlock startCatch,
int finallytype, HashMap<BasicBlock, Boolean> mapLast, boolean skippedFirst) {
class BlockStackEntry {
public BasicBlock blockCatch;
public BasicBlock blockSample;
// TODO: correct handling (merging) of multiple paths
public List<int[]> lstStoreVars;
public BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {
this.blockCatch = blockCatch;
this.blockSample = blockSample;
this.lstStoreVars = new ArrayList<int[]>(lstStoreVars);
}
}
List<BlockStackEntry> stack = new LinkedList<BlockStackEntry>();
HashSet<BasicBlock> setSample = new HashSet<BasicBlock>();
HashMap<String, BasicBlock[]> mapNext = new HashMap<String, BasicBlock[]>();
stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<int[]>()));
while(!stack.isEmpty()) {
BlockStackEntry entry = stack.remove(0);
BasicBlock blockCatch = entry.blockCatch;
BasicBlock blockSample = entry.blockSample;
boolean isFirstBlock = !skippedFirst && blockCatch == startCatch;
boolean isLastBlock = mapLast.containsKey(blockCatch);
boolean isTrueLastBlock = isLastBlock && mapLast.get(blockCatch);
if(!compareBasicBlocksEx(graph, blockCatch, blockSample, (isFirstBlock?1:0) | (isTrueLastBlock?2:0), finallytype, entry.lstStoreVars)) {
return null;
}
if(blockSample.getSuccs().size() != blockCatch.getSuccs().size()) {
return null;
}
setSample.add(blockSample);
// direct successors
for(int i=0;i<blockCatch.getSuccs().size();i++) {
BasicBlock sucCatch = blockCatch.getSuccs().get(i);
BasicBlock sucSample = blockSample.getSuccs().get(i);
if(catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {
stack.add(new BlockStackEntry(sucCatch, sucSample, entry.lstStoreVars));
}
}
// exception successors
if(isLastBlock && blockSample.getSeq().isEmpty()) {
; // do nothing, blockSample will be removed anyway
} else {
if(blockCatch.getSuccExceptions().size() == blockSample.getSuccExceptions().size()) {
for(int i=0;i<blockCatch.getSuccExceptions().size();i++) {
BasicBlock sucCatch = blockCatch.getSuccExceptions().get(i);
BasicBlock sucSample = blockSample.getSuccExceptions().get(i);
String excCatch = graph.getExceptionRange(sucCatch, blockCatch).getUniqueExceptionsString();
String excSample = graph.getExceptionRange(sucSample, blockSample).getUniqueExceptionsString();
// FIXME: compare handlers if possible
boolean equalexc = excCatch == null?excSample == null:excCatch.equals(excSample);
if(equalexc) {
if(catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {
List<int[]> lst = entry.lstStoreVars;
if(sucCatch.getSeq().length() > 0 && sucSample.getSeq().length() > 0) {
Instruction instrCatch = sucCatch.getSeq().getInstr(0);
Instruction instrSample = sucSample.getSeq().getInstr(0);
if(instrCatch.opcode == CodeConstants.opc_astore &&
instrSample.opcode == CodeConstants.opc_astore) {
lst = new ArrayList<int[]>(lst);
lst.add(new int[]{instrCatch.getOperand(0), instrSample.getOperand(0)});
}
}
stack.add(new BlockStackEntry(sucCatch, sucSample, lst));
}
} else {
return null;
}
}
} else {
return null;
}
}
if(isLastBlock) {
HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(blockSample.getSuccs());
setSuccs.removeAll(setSample);
for(BlockStackEntry stackent : stack) {
setSuccs.remove(stackent.blockSample);
}
for(BasicBlock succ : setSuccs) {
if(graph.getLast() != succ) { // FIXME: why?
mapNext.put(blockSample.id+"#"+succ.id, new BasicBlock[] {blockSample, succ, isTrueLastBlock?succ:null});
}
}
}
}
return new Object[]{setSample, getUniqueNext(graph, new HashSet<BasicBlock[]>(mapNext.values()))};
}
private BasicBlock getUniqueNext(ControlFlowGraph graph, HashSet<BasicBlock[]> setNext) {
// precondition: there is at most one true exit path in a finally statement
BasicBlock next = null;
boolean multiple = false;
for(BasicBlock[] arr : setNext) {
if(arr[2] != null) {
next = arr[1];
multiple = false;
break;
} else {
if(next == null) {
next = arr[1];
} else if(next != arr[1]) {
multiple = true;
}
if(arr[1].getPreds().size() == 1) {
next = arr[1];
}
}
}
if(multiple) { // TODO: generic solution
for(BasicBlock[] arr : setNext) {
BasicBlock block = arr[1];
if(block != next) {
if(InterpreterUtil.equalSets(next.getSuccs(), block.getSuccs())) {
InstructionSequence seqNext = next.getSeq();
InstructionSequence seqBlock = block.getSeq();
if(seqNext.length() == seqBlock.length()) {
for(int i=0;i<seqNext.length();i++) {
Instruction instrNext = seqNext.getInstr(i);
Instruction instrBlock = seqBlock.getInstr(i);
if(instrNext.opcode != instrBlock.opcode || instrNext.wide != instrBlock.wide
|| instrNext.operandsCount() != instrBlock.operandsCount()) {
return null;
}
for(int j=0;i<instrNext.getOperands().length;j++) {
if(instrNext.getOperand(j) != instrBlock.getOperand(j)) {
return null;
}
}
}
} else {
return null;
}
} else {
return null;
}
}
}
// try {
// DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
// } catch(IOException ex) {
// ex.printStackTrace();
// }
for(BasicBlock[] arr : setNext) {
if(arr[1] != next) {
// FIXME: exception edge possible?
arr[0].removeSuccessor(arr[1]);
arr[0].addSuccessor(next);
}
}
DeadCodeHelper.removeDeadBlocks(graph);
}
return next;
}
private boolean compareBasicBlocksEx(ControlFlowGraph graph, BasicBlock pattern, BasicBlock sample, int type, int finallytype, List<int[]> lstStoreVars) {
InstructionSequence seqPattern = pattern.getSeq();
InstructionSequence seqSample = sample.getSeq();
if(type!=0) {
seqPattern = seqPattern.clone();
if((type & 1) > 0) { // first
if(finallytype > 0) {
seqPattern.removeInstruction(0);
}
}
if((type & 2) > 0) { // last
if(finallytype == 0 || finallytype == 2) {
seqPattern.removeInstruction(seqPattern.length()-1);
}
if(finallytype == 2) {
seqPattern.removeInstruction(seqPattern.length()-1);
}
}
}
if(seqPattern.length() > seqSample.length()) {
return false;
}
for(int i=0;i<seqPattern.length();i++) {
Instruction instrPattern = seqPattern.getInstr(i);
Instruction instrSample = seqSample.getInstr(i);
// compare instructions with respect to jumps
if(!equalInstructions(instrPattern, instrSample, lstStoreVars)) {
return false;
}
}
if(seqPattern.length() < seqSample.length()) { // split in two blocks
SimpleInstructionSequence seq = new SimpleInstructionSequence();
for(int i=seqSample.length()-1;i>=seqPattern.length();i--) {
seq.addInstruction(0, seqSample.getInstr(i), -1);
seqSample.removeInstruction(i);
}
BasicBlock newblock = new BasicBlock(++graph.last_id);
newblock.setSeq(seq);
List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
lstTemp.addAll(sample.getSuccs());
// move successors
for(BasicBlock suc: lstTemp) {
sample.removeSuccessor(suc);
newblock.addSuccessor(suc);
}
sample.addSuccessor(newblock);
graph.getBlocks().addWithKey(newblock, newblock.id);
HashSet<BasicBlock> setFinallyExits = graph.getFinallyExits();
if(setFinallyExits.contains(sample)) {
setFinallyExits.remove(sample);
setFinallyExits.add(newblock);
}
// copy exception edges and extend protected ranges
for(int j=0;j<sample.getSuccExceptions().size();j++) {
BasicBlock hd = sample.getSuccExceptions().get(j);
newblock.addSuccessorException(hd);
ExceptionRangeCFG range = graph.getExceptionRange(hd, sample);
range.getProtectedRange().add(newblock);
}
}
return true;
}
public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) {
if(first.opcode != second.opcode || first.wide != second.wide
|| first.operandsCount() != second.operandsCount()) {
return false;
}
if(first.group != CodeConstants.GROUP_JUMP && first.getOperands() != null) { // FIXME: switch comparison
for(int i=0;i<first.getOperands().length;i++) {
int firstOp = first.getOperand(i);
int secondOp = second.getOperand(i);
if(firstOp != secondOp) {
// a-load/store instructions
if(first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) {
for(int[] arr : lstStoreVars) {
if(arr[0] == firstOp && arr[1] == secondOp) {
return true;
}
}
}
return false;
}
}
}
return true;
}
private void deleteArea(ControlFlowGraph graph, Object[] area) {
BasicBlock start = (BasicBlock)area[0];
BasicBlock next = (BasicBlock)area[2];
if(start == next) {
return;
}
if(next == null) {
// dummy exit block
next = graph.getLast();
}
// collect common exception ranges of predecessors and successors
HashSet<BasicBlock> setCommonExceptionHandlers = new HashSet<BasicBlock>(next.getSuccExceptions());
for(BasicBlock pred : start.getPreds()) {
setCommonExceptionHandlers.retainAll(pred.getSuccExceptions());
}
boolean is_outside_range = false;
HashSet<BasicBlock> setPredecessors = new HashSet<BasicBlock>(start.getPreds());
// replace start with next
for(BasicBlock pred: setPredecessors) {
pred.replaceSuccessor(start, next);
}
HashSet<BasicBlock> setBlocks = (HashSet<BasicBlock>)area[1];
HashSet<ExceptionRangeCFG> setCommonRemovedExceptionRanges = null;
// remove all the blocks inbetween
for(BasicBlock block: setBlocks) {
// artificial basic blocks (those resulted from splitting)
// can belong to more than one area
if(graph.getBlocks().containsKey(block.id)) {
if(!block.getSuccExceptions().containsAll(setCommonExceptionHandlers)) {
is_outside_range = true;
}
HashSet<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<ExceptionRangeCFG>();
for(BasicBlock handler : block.getSuccExceptions()) {
setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block));
}
if(setCommonRemovedExceptionRanges == null) {
setCommonRemovedExceptionRanges = setRemovedExceptionRanges;
} else {
setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges);
}
// shift extern edges on splitted blocks
if(block.getSeq().isEmpty() && block.getSuccs().size() == 1) {
BasicBlock succs = block.getSuccs().get(0);
for(BasicBlock pred : new ArrayList<BasicBlock>(block.getPreds())) {
if(!setBlocks.contains(pred)) {
pred.replaceSuccessor(block, succs);
}
}
if(graph.getFirst() == block) {
graph.setFirst(succs);
}
}
graph.removeBlock(block);
}
}
if(is_outside_range) {
// new empty block
BasicBlock emptyblock = new BasicBlock(++graph.last_id);
emptyblock.setSeq(new SimpleInstructionSequence());
graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
// add to ranges if necessary
if(setCommonRemovedExceptionRanges != null) {
for(ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {
emptyblock.addSuccessorException(range.getHandler());
range.getProtectedRange().add(emptyblock);
}
}
// insert between predecessors and next
emptyblock.addSuccessor(next);
for(BasicBlock pred: setPredecessors) {
pred.replaceSuccessor(next, emptyblock);
}
}
}
private void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) {
InstructionSequence seq = block.getSeq();
if(finallytype == 3) { // empty finally handler
for(int i=seq.length()-1;i>=0;i--) {
seq.removeInstruction(i);
}
} else {
if((blocktype & 1) > 0) { // first
if(finallytype == 2 || finallytype == 1) { // astore or pop
seq.removeInstruction(0);
}
}
if((blocktype & 2) > 0) { // last
if(finallytype == 2 || finallytype == 0) {
seq.removeInstruction(seq.length()-1);
}
if(finallytype == 2) { // astore
seq.removeInstruction(seq.length()-1);
}
}
}
}
}