diff options
author | Roman Shevchenko <roman.shevchenko@jetbrains.com> | 2014-08-28 20:52:43 +0400 |
---|---|---|
committer | Roman Shevchenko <roman.shevchenko@jetbrains.com> | 2014-08-28 20:52:43 +0400 |
commit | 663631f0456fcc245dd835889f86541d75161c53 (patch) | |
tree | e183fa9777242e2900ff3648a726f05b190bc51b /src/org/jetbrains/java/decompiler/modules | |
parent | f864084061806fda5510e50bfd2e69bf1dea406b (diff) | |
download | fernflower-663631f0456fcc245dd835889f86541d75161c53.tar fernflower-663631f0456fcc245dd835889f86541d75161c53.tar.gz fernflower-663631f0456fcc245dd835889f86541d75161c53.tar.lz fernflower-663631f0456fcc245dd835889f86541d75161c53.tar.xz fernflower-663631f0456fcc245dd835889f86541d75161c53.zip |
java-decompiler: post-import cleanup (classes moved)
Diffstat (limited to 'src/org/jetbrains/java/decompiler/modules')
77 files changed, 21650 insertions, 0 deletions
diff --git a/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java new file mode 100644 index 0000000..4d39e68 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java @@ -0,0 +1,438 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.code; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; +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; + +public class DeadCodeHelper { + + public static void removeDeadBlocks(ControlFlowGraph graph) { + + LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); + HashSet<BasicBlock> setStacked = new HashSet<BasicBlock>(); + + stack.add(graph.getFirst()); + setStacked.add(graph.getFirst()); + + while(!stack.isEmpty()) { + BasicBlock block = stack.removeFirst(); + + List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); + lstSuccs.addAll(block.getSuccExceptions()); + + for(BasicBlock succ : lstSuccs) { + if(!setStacked.contains(succ)) { + stack.add(succ); + setStacked.add(succ); + } + } + } + + HashSet<BasicBlock> setAllBlocks = new HashSet<BasicBlock>(graph.getBlocks()); + setAllBlocks.removeAll(setStacked); + + for(BasicBlock block : setAllBlocks) { + graph.removeBlock(block); + } + } + + public static void removeEmptyBlocks(ControlFlowGraph graph) { + + List<BasicBlock> blocks = graph.getBlocks(); + + boolean cont; + do { + cont = false; + + for(int i=blocks.size()-1;i>=0;i--) { + BasicBlock block = (BasicBlock)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<BasicBlock> setExits = new HashSet<BasicBlock>(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<BasicBlock> setPreds = new HashSet<BasicBlock>(block.getPreds()); + HashSet<BasicBlock> setSuccs = new HashSet<BasicBlock>(block.getSuccs()); + + // collect common exception ranges of predecessors and successors + HashSet<BasicBlock> setCommonExceptionHandlers = null; + for(int i = 0; i < 2; ++i) { + for(BasicBlock pred : i == 0 ? setPreds : setSuccs) { + if(setCommonExceptionHandlers == null) { + setCommonExceptionHandlers = new HashSet<BasicBlock>(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<ExceptionRangeCFG> lstRanges = graph.getExceptions(); + for(int i=lstRanges.size()-1;i>=0;i--) { + ExceptionRangeCFG range = lstRanges.get(i); + List<BasicBlock> 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<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(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 + HashSet<BasicBlock> 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) { + DeadCodeHelper.removeDeadBlocks(graph); + } + } + } + + return deletedRanges; + } + + + public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) { + + HashSet<BasicBlock> marked = new HashSet<BasicBlock>(); + + if(block == dom) { + return true; + } + + LinkedList<BasicBlock> lstNodes = new LinkedList<BasicBlock>(); + lstNodes.add(block); + + while(!lstNodes.isEmpty()) { + + BasicBlock node = (BasicBlock)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 = (BasicBlock)node.getPreds().get(i); + if(!marked.contains(pred) && pred != dom) { + lstNodes.add(pred); + } + } + + for(int i=0;i<node.getPredExceptions().size();i++) { + BasicBlock pred = (BasicBlock)node.getPredExceptions().get(i); + if(!marked.contains(pred) && pred != dom) { + lstNodes.add(pred); + } + } + } + + return true; + } + + public static void removeGotos(ControlFlowGraph graph) { + + for(BasicBlock block : graph.getBlocks()) { + Instruction instr = block.getLastInstruction(); + + if(instr!=null && instr.opcode == CodeConstants.opc_goto) { + block.getSeq().removeInstruction(block.getSeq().length()-1); + } + } + + DeadCodeHelper.removeEmptyBlocks(graph); + } + + public static void connectDummyExitBlock(ControlFlowGraph graph) { + + BasicBlock exit = graph.getLast(); + for(BasicBlock block : new HashSet<BasicBlock>(exit.getPreds())) { + exit.removePredecessor(block); + block.addSuccessor(exit); + } + } + + 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<BasicBlock> setPredHandlersUnion = new HashSet<BasicBlock>(); + HashSet<BasicBlock> setPredHandlersIntersection = new HashSet<BasicBlock>(); + + 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 + setPredHandlersIntersection.removeAll(block.getSuccExceptions()); + 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<BasicBlock> setRangesToBeRemoved = new HashSet<BasicBlock>(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<BasicBlock>(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) { + + for(;;) { + + boolean merged = false; + + 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()); + next.getSeq().clear(); + + removeEmptyBlock(graph, next, true); + + merged = true; + break; + } + } + } + } + + } + + if(!merged) { + break; + } + } + + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java new file mode 100644 index 0000000..aedca91 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java @@ -0,0 +1,41 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.LinkedList; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class ClearStructHelper { + + public static void clearStatements(RootStatement root) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while(!stack.isEmpty()) { + + Statement stat = stack.removeFirst(); + + stat.clearTempInformation(); + + stack.addAll(stat.getStats()); + } + + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java new file mode 100644 index 0000000..a07346b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java @@ -0,0 +1,213 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.List; + + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class ConcatenationHelper { + + private static final String builderClass = "java/lang/StringBuilder"; + private static final String bufferClass = "java/lang/StringBuffer"; + private static final String stringClass = "java/lang/String"; + + private static final VarType builderType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuilder"); + private static final VarType bufferType = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/StringBuffer"); + + + public static Exprent contractStringConcat(Exprent expr) { + + Exprent exprTmp = null; + VarType cltype = null; + + // first quick test + if(expr.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent iex = (InvocationExprent)expr; + if("toString".equals(iex.getName())) { + if(builderClass.equals(iex.getClassname())) { + cltype = builderType; + } else if(bufferClass.equals(iex.getClassname())) { + cltype = bufferType; + } + if(cltype!=null) { + exprTmp = iex.getInstance(); + } + } + } + + if(exprTmp == null) { + return expr; + } + + + // iterate in depth, collecting possible operands + List<Exprent> lstOperands = new ArrayList<Exprent>(); + + for(;;) { + + int found = 0; + + switch(exprTmp.type) { + case Exprent.EXPRENT_INVOCATION: + InvocationExprent iex = (InvocationExprent)exprTmp; + if(isAppendConcat(iex, cltype)) { + lstOperands.add(0, iex.getLstParameters().get(0)); + exprTmp = iex.getInstance(); + found = 1; + } + break; + case Exprent.EXPRENT_NEW: + NewExprent nex = (NewExprent)exprTmp; + if(isNewConcat(nex, cltype)) { + VarType[] params = nex.getConstructor().getDescriptor().params; + if(params.length == 1) { + lstOperands.add(0, nex.getConstructor().getLstParameters().get(0)); + } + found = 2; + } + } + + if(found == 0) { + return expr; + } else if(found == 2) { + break; + } + } + + int first2str = 0; + int index=0; + while(index<lstOperands.size() && index<2) { + if(lstOperands.get(index).getExprType().equals(VarType.VARTYPE_STRING)) { + first2str |= (index+1); + } + index++; + } + + if(first2str == 0) { + lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, "")); + } + + // remove redundant String.valueOf + for(int i=0;i<lstOperands.size();i++) { + Exprent rep = removeStringValueOf(lstOperands.get(i)); + + boolean ok = (i>1); + if(!ok) { + boolean isstr = rep.getExprType().equals(VarType.VARTYPE_STRING); + ok = isstr || first2str != i+1; + + if(i == 0) { + first2str &= 2; + } + } + + if(ok) { + lstOperands.set(i, rep); + } + } + + // build exprent to return + Exprent func = lstOperands.get(0); + + for(int i=1;i<lstOperands.size();i++) { + List<Exprent> lstTmp = new ArrayList<Exprent>(); + lstTmp.add(func); + lstTmp.add(lstOperands.get(i)); + func = new FunctionExprent(FunctionExprent.FUNCTION_STRCONCAT, lstTmp); + } + + return func; + + } + + private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) { + + if("append".equals(expr.getName())) { + MethodDescriptor md = expr.getDescriptor(); + if(md.ret.equals(cltype) && md.params.length == 1) { + VarType param = md.params[0]; + switch(param.type) { + case CodeConstants.TYPE_OBJECT: + if(!param.equals(VarType.VARTYPE_STRING) && + !param.equals(VarType.VARTYPE_OBJECT)) { + break; + } + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_DOUBLE: + case CodeConstants.TYPE_FLOAT: + case CodeConstants.TYPE_INT: + case CodeConstants.TYPE_LONG: + return true; + default: + } + } + } + + return false; + } + + private static boolean isNewConcat(NewExprent expr, VarType cltype) { + + if(expr.getNewtype().equals(cltype)) { + VarType[] params = expr.getConstructor().getDescriptor().params; + if(params.length == 0 || (params.length == 1 && + params[0].equals(VarType.VARTYPE_STRING))) { + return true; + } + } + + return false; + } + + private static Exprent removeStringValueOf(Exprent exprent) { + + if(exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent iex = (InvocationExprent)exprent; + if("valueOf".equals(iex.getName()) && stringClass.equals(iex.getClassname())) { + MethodDescriptor md = iex.getDescriptor(); + if(md.params.length == 1) { + VarType param = md.params[0]; + switch(param.type) { + case CodeConstants.TYPE_OBJECT: + if(!param.equals(VarType.VARTYPE_OBJECT)) { + break; + } + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_DOUBLE: + case CodeConstants.TYPE_FLOAT: + case CodeConstants.TYPE_INT: + case CodeConstants.TYPE_LONG: + return iex.getLstParameters().get(0); + } + } + } + } + + return exprent; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java new file mode 100644 index 0000000..ea8823a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java @@ -0,0 +1,217 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class DecHelper { + + public static boolean checkStatementExceptions(List<Statement> lst) { + + Set<Statement> all = new HashSet<Statement>(lst); + + Set<Statement> handlers = new HashSet<Statement>(); + Set<Statement> intersection = null; + + for(Statement stat : lst) { + Set<Statement> setNew = stat.getNeighboursSet(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD); + + if(intersection == null) { + intersection = setNew; + } else { + HashSet<Statement> interclone = new HashSet<Statement>(intersection); + interclone.removeAll(setNew); + + intersection.retainAll(setNew); + + setNew.removeAll(intersection); + + handlers.addAll(interclone); + handlers.addAll(setNew); + } + } + + for(Statement stat : handlers) { + if(!all.contains(stat) || !all.containsAll(stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD))) { + return false; + } + } + + // check for other handlers (excluding head) + for(int i=1;i<lst.size();i++) { + Statement stat = lst.get(i); + if(!stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() && !handlers.contains(stat)) { + return false; + } + } + + return true; + } + + public static boolean isChoiceStatement(Statement head, List<Statement> lst) { + + Statement post = null; + + Set<Statement> setDest = head.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); + + if(setDest.contains(head)) { + return false; + } + + for(;;) { + + lst.clear(); + + boolean repeat = false; + + setDest.remove(post); + Iterator<Statement> it = setDest.iterator(); + + while(it.hasNext()) { + Statement stat = it.next(); + + if(stat.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { + if(post == null) { + post = stat; + repeat = true; + break; + } else { + return false; + } + } + + // preds + Set<Statement> setPred = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + setPred.remove(head); + if(setPred.contains(stat)) { + return false; + } + + if(!setDest.containsAll(setPred) || setPred.size()>1) { + if(post == null) { + post = stat; + repeat = true; + break; + } else { + return false; + } + } else if(setPred.size() == 1) { + Statement pred = setPred.iterator().next(); + while(lst.contains(pred)) { + Set<Statement> setPredTemp = pred.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + setPredTemp.remove(head); + + if(!setPredTemp.isEmpty()) { // at most 1 predecessor + pred = setPredTemp.iterator().next(); + if(pred == stat) { + return false; // loop found + } + } else { + break; + } + } + } + + // succs + List<StatEdge> lstEdges = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); + if(lstEdges.size() > 1) { + Set<Statement> setSucc = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); + setSucc.retainAll(setDest); + + if(setSucc.size()>0) { + return false; + } else { + if(post == null) { + post = stat; + repeat = true; + break; + } else { + return false; + } + } + } else if(lstEdges.size() == 1) { + + StatEdge edge = lstEdges.get(0); + if(edge.getType() == StatEdge.TYPE_REGULAR) { + Statement statd = edge.getDestination(); + if(head == statd) { + return false; + } + if(!setDest.contains(statd) && post!=statd) { + if(post!=null) { + return false; + } else { + Set<Statement> set = statd.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + if(set.size()>1){ + post = statd; + repeat = true; + break; + } else { + return false; + } + } + } + } + } + + lst.add(stat); + } + + if(!repeat) { + break; + } + + } + + lst.add(head); + lst.remove(post); + + lst.add(0, post); + + return true; + + } + + + public static HashSet<Statement> getUniquePredExceptions(Statement head) { + + HashSet<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + + Iterator<Statement> it = setHandlers.iterator(); + while(it.hasNext()) { + if(it.next().getPredecessorEdges(StatEdge.TYPE_EXCEPTION).size()>1) { + it.remove(); + } + } + return setHandlers; + } + + public static List<Exprent> copyExprentList(List<Exprent> lst) { + List<Exprent> ret = new ArrayList<Exprent>(); + for(Exprent expr: lst) { + ret.add(expr.copy()); + } + return ret; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java new file mode 100644 index 0000000..ba6f839 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -0,0 +1,720 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +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.IFernflowerLogger; +import org.jetbrains.java.decompiler.modules.decompiler.decompose.FastExtendedPostdominanceHelper; +import org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.IrreducibleCFGDeobfuscator; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.GeneralStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.util.FastFixedSetFactory; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.VBStyleCollection; +import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet; + +public class DomHelper { + + + private static RootStatement graphToStatement(ControlFlowGraph graph) { + + VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); + VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks(); + + for(BasicBlock block: blocks) { + stats.addWithKey(new BasicBlockStatement(block), block.id); + } + + BasicBlock firstblock = graph.getFirst(); + // head statement + Statement firstst = stats.getWithKey(firstblock.id); + // dummy exit statement + Statement dummyexit = new Statement(); + dummyexit.type = Statement.TYPE_DUMMYEXIT; + + Statement general; + if(stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block + general = new GeneralStatement(firstst, stats, null); + } else { // one straightforward basic block + RootStatement root = new RootStatement(firstst, dummyexit); + firstst.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, firstst, dummyexit, root)); + + return root; + } + + for(BasicBlock block: blocks) { + Statement stat = stats.getWithKey(block.id); + + for(BasicBlock succ: block.getSuccs()) { + Statement stsucc = stats.getWithKey(succ.id); + + int type; + if(stsucc==firstst) { + type = StatEdge.TYPE_CONTINUE; + } else if(graph.getFinallyExits().contains(block)) { + type = StatEdge.TYPE_FINALLYEXIT; + stsucc = dummyexit; + } else if(succ.id == graph.getLast().id) { + type = StatEdge.TYPE_BREAK; + stsucc = dummyexit; + } else { + type = StatEdge.TYPE_REGULAR; + } + + stat.addSuccessor(new StatEdge(type, stat, (type == StatEdge.TYPE_CONTINUE)?general:stsucc, + (type == StatEdge.TYPE_REGULAR)?null:general)); + } + + // exceptions edges + for(BasicBlock succex: block.getSuccExceptions()) { + Statement stsuccex = stats.getWithKey(succex.id); + + ExceptionRangeCFG range = graph.getExceptionRange(succex, block); + if(!range.isCircular()) { + stat.addSuccessor(new StatEdge(stat, stsuccex, range.getExceptionTypes())); + } + } + + } + + general.buildContinueSet(); + general.buildMonitorFlags(); + return new RootStatement(general, dummyexit); + } + + public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) { + + HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<Statement, FastFixedSet<Statement>>(); + + StrongConnectivityHelper schelper = new StrongConnectivityHelper(container); + List<List<Statement>> components = schelper.getComponents(); + + List<Statement> lstStats = container.getPostReversePostOrderList(StrongConnectivityHelper.getExitReps(components)); + + FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<Statement>(lstStats); + + FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet(); + setFlagNodes.setAllElements(); + + FastFixedSet<Statement> initSet = factory.spawnEmptySet(); + initSet.setAllElements(); + + for(List<Statement> lst: components) { + FastFixedSet<Statement> tmpSet; + + if(StrongConnectivityHelper.isExitComponent(lst)) { + tmpSet = factory.spawnEmptySet(); + tmpSet.addAll(lst); + } else { + tmpSet = initSet.getCopy(); + } + + for(Statement stat : lst) { + lists.put(stat, tmpSet); + } + } + + do { + + for(Statement stat : lstStats) { + + if(!setFlagNodes.contains(stat)) { + continue; + } + setFlagNodes.remove(stat); + + FastFixedSet<Statement> doms = lists.get(stat); + FastFixedSet<Statement> domsSuccs = factory.spawnEmptySet(); + + List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); + for(int j=0;j<lstSuccs.size();j++) { + Statement succ = lstSuccs.get(j); + FastFixedSet<Statement> succlst = lists.get(succ); + + if(j == 0) { + domsSuccs.union(succlst); + } else { + domsSuccs.intersection(succlst); + } + } + + if(!domsSuccs.contains(stat)) { + domsSuccs.add(stat); + } + + if(!InterpreterUtil.equalObjects(domsSuccs, doms)) { + + lists.put(stat, domsSuccs); + + List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + for(Statement pred : lstPreds) { + setFlagNodes.add(pred); + } + } + } + + } while(!setFlagNodes.isEmpty()); + + VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<List<Integer>, Integer>(); + List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial! + + final HashMap<Integer, Integer> mapSortOrder = new HashMap<Integer, Integer>(); + for(int i=0;i<lstRevPost.size();i++) { + mapSortOrder.put(lstRevPost.get(i).id, i); + } + + for(Statement st: lstStats) { + + List<Integer> lstPosts = new ArrayList<Integer>(); + for(Statement stt : lists.get(st)) { + lstPosts.add(stt.id); + } + + Collections.sort(lstPosts, new Comparator<Integer>() { + public int compare(Integer o1, Integer o2) { + return mapSortOrder.get(o1).compareTo(mapSortOrder.get(o2)); + } + }); + + if(lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) { + lstPosts.add(lstPosts.remove(0)); + } + + ret.addWithKey(lstPosts, st.id); + } + + return ret; + } + + public static RootStatement parseGraph(ControlFlowGraph graph) { + + RootStatement root = graphToStatement(graph); + + if(!processStatement(root, new HashMap<Integer, Set<Integer>>())) { + DecompilerContext.getLogger().writeMessage("parsing failure!", IFernflowerLogger.ERROR); + +// try { +// DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot")); +// } catch (Exception ex) { +// ex.printStackTrace(); +// } + throw new RuntimeException("parsing failure!"); + } + + LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); + + SequenceHelper.condenseSequences(root); + root.buildMonitorFlags(); + + // build synchronized statements + buildSynchronized(root); + + return root; + } + + public static void removeSynchronizedHandler(Statement stat) { + + for(Statement st : stat.getStats()) { + removeSynchronizedHandler(st); + } + + if(stat.type == Statement.TYPE_SYNCRONIZED) { + ((SynchronizedStatement)stat).removeExc(); + } + } + + + private static void buildSynchronized(Statement stat) { + + for(Statement st : stat.getStats()) { + buildSynchronized(st); + } + + if(stat.type == Statement.TYPE_SEQUENCE) { + + for(;;) { + + boolean found = false; + + List<Statement> lst = stat.getStats(); + for(int i=0;i<lst.size()-1;i++) { + Statement current = lst.get(i); // basic block + + if(current.isMonitorEnter()) { + + Statement next = lst.get(i+1); + Statement nextDirect = next; + + while(next.type == Statement.TYPE_SEQUENCE) { + next = next.getFirst(); + } + + if(next.type == Statement.TYPE_CATCHALL) { + + CatchAllStatement ca = (CatchAllStatement)next; + + if(ca.getFirst().isContainsMonitorExit() && ca.getHandler().isContainsMonitorExit()) { + + // remove the head block from sequence + current.removeSuccessor(current.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); + + for(StatEdge edge: current.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + current.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, nextDirect); + nextDirect.addPredecessor(edge); + } + + stat.getStats().removeWithKey(current.id); + stat.setFirst(stat.getStats().get(0)); + + // new statement + SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler()); + sync.setAllParent(); + + for(StatEdge edge : new HashSet<StatEdge>(ca.getLabelEdges())) { + sync.addLabeledEdge(edge); + } + + current.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, current, ca.getFirst())); + + ca.getParent().replaceStatement(ca, sync); + found = true; + break; + } + } + } + } + + if(!found) { + break; + } + } + + } + + } + + private static boolean processStatement(Statement general, HashMap<Integer, Set<Integer>> mapExtPost) { + + if(general.type == Statement.TYPE_ROOT) { + Statement stat = general.getFirst(); + if(stat.type != Statement.TYPE_GENERAL) { + return true; + } else { + boolean complete = processStatement(stat, mapExtPost); + if(complete) { + // replace general purpose statement with simple one + general.replaceStatement(stat, stat.getFirst()); + } + return complete; + } + } + + boolean mapRefreshed = mapExtPost.isEmpty(); + + for(int mapstage = 0; mapstage < 2; mapstage++) { + + for(int reducibility=0; reducibility < 5; reducibility++) { // FIXME: implement proper node splitting. For now up to 5 nodes in sequence are splitted. + + if(reducibility > 0) { + +// try { +// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); +// } catch(Exception ex) {ex.printStackTrace();} + + // take care of irreducible control flow graphs + if(IrreducibleCFGDeobfuscator.isStatementIrreducible(general)) { + if(!IrreducibleCFGDeobfuscator.splitIrreducibleNode(general)) { + DecompilerContext.getLogger().writeMessage("Irreducible statement cannot be decomposed!", IFernflowerLogger.ERROR); + break; + } + } else { + if(mapstage == 2 || mapRefreshed) { // last chance lost + DecompilerContext.getLogger().writeMessage("Statement cannot be decomposed although reducible!", IFernflowerLogger.ERROR); + } + break; + } + +// try { +// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); +// } catch(Exception ex) {ex.printStackTrace();} + + mapExtPost = new HashMap<Integer, Set<Integer>>(); + mapRefreshed = true; + } + + for(int i=0;i<2;i++) { + + boolean forceall = i!=0; + + for(;;) { + + if(findSimpleStatements(general, mapExtPost)) { + reducibility = 0; + } + + if(general.type == Statement.TYPE_PLACEHOLDER) { + return true; + } + + Statement stat = findGeneralStatement(general, forceall, mapExtPost); + + if(stat!=null) { + boolean complete = processStatement(stat, general.getFirst() == stat?mapExtPost:new HashMap<Integer, Set<Integer>>()); + + if(complete) { + // replace general purpose statement with simple one + general.replaceStatement(stat, stat.getFirst()); + } else { + return false; + } + + mapExtPost = new HashMap<Integer, Set<Integer>>(); + mapRefreshed = true; + reducibility = 0; + + } else { + break; + } + } + } + +// try { +// DotExporter.toDotFile(general, new File("c:\\Temp\\stat1.dot")); +// } catch (Exception ex) { +// ex.printStackTrace(); +// } + } + + if(mapRefreshed) { + break; + } else { + mapExtPost = new HashMap<Integer, Set<Integer>>(); + } + } + + return false; + } + + private static Statement findGeneralStatement(Statement stat, boolean forceall, HashMap<Integer, Set<Integer>> mapExtPost) { + + VBStyleCollection<Statement, Integer> stats = stat.getStats(); + VBStyleCollection<List<Integer>, Integer> vbPost; + + if(mapExtPost.isEmpty()) { + FastExtendedPostdominanceHelper extpost = new FastExtendedPostdominanceHelper(); + mapExtPost.putAll(extpost.getExtendedPostdominators(stat)); + } + + if(forceall) { + vbPost = new VBStyleCollection<List<Integer>, Integer>(); + List<Statement> lstAll = stat.getPostReversePostOrderList(); + + for(Statement st: lstAll) { + Set<Integer> set = mapExtPost.get(st.id); + if(set != null) { + vbPost.addWithKey(new ArrayList<Integer>(set), st.id); // FIXME: sort order!! + } + } + + // tail statements + Set<Integer> setFirst = mapExtPost.get(stat.getFirst().id); + if(setFirst != null) { + for(Integer id : setFirst) { + List<Integer> lst = vbPost.getWithKey(id); + if(lst == null) { + vbPost.addWithKey(lst = new ArrayList<Integer>(), id); + } + lst.add(id); + } + } + + } else { + vbPost = calcPostDominators(stat); + } + + for(int k=0;k<vbPost.size();k++) { + + Integer headid = vbPost.getKey(k); + List<Integer> posts = vbPost.get(k); + + if(!mapExtPost.containsKey(headid) && + !(posts.size() == 1 && posts.get(0).equals(headid))) { + continue; + } + + Statement head = stats.getWithKey(headid); + + Set<Integer> setExtPosts = mapExtPost.get(headid); + + for(int i=0;i<posts.size();i++) { + + Integer postid = posts.get(i); + if(!postid.equals(headid) && !setExtPosts.contains(postid)) { + continue; + } + + Statement post = stats.getWithKey(postid); + + if(post == null) { // possible in case of an inherited postdominance set + continue; + } + + boolean same = (post == head); + + HashSet<Statement> setNodes = new HashSet<Statement>(); + HashSet<Statement> setPreds = new HashSet<Statement>(); + + // collect statement nodes + HashSet<Statement> setHandlers = new HashSet<Statement>(); + setHandlers.add(head); + for(;;) { + + boolean hdfound = false; + Iterator<Statement> itHandlers = setHandlers.iterator(); + while(itHandlers.hasNext()) { + Statement handler = itHandlers.next(); + + if(setNodes.contains(handler)) { + continue; + } + + boolean addhd = (setNodes.size() == 0); // first handler == head + if(!addhd) { + List<Statement> hdsupp = handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); + addhd = (setNodes.containsAll(hdsupp) && (setNodes.size() > hdsupp.size() + || setNodes.size() == 1)); // strict subset + } + + if(addhd) { + LinkedList<Statement> lstStack = new LinkedList<Statement>(); + lstStack.add(handler); + + while(!lstStack.isEmpty()) { + Statement st = lstStack.remove(0); + + if(!(setNodes.contains(st) || (!same && st == post))) { + setNodes.add(st); + if(st != head) { + // record predeccessors except for the head + setPreds.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD)); + } + + // put successors on the stack + lstStack.addAll(st.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); + + // exception edges + setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + } + } + + hdfound = true; + setHandlers.remove(handler); + break; + } + } + + if(!hdfound) { + break; + } + } + + // check exception handlers + setHandlers.clear(); + for(Statement st : setNodes) { + setHandlers.addAll(st.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_FORWARD)); + } + setHandlers.removeAll(setNodes); + + boolean excok = true; + Iterator<Statement> itt = setHandlers.iterator(); + while(itt.hasNext()) { + Statement handler = itt.next(); + if(!handler.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD).containsAll(setNodes)) { + excok = false; + break; + } + } + + // build statement and return + if(excok) { + Statement res = null; + + setPreds.removeAll(setNodes); + if(setPreds.size() == 0) { + if((setNodes.size() > 1 || + head.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).contains(head)) + && setNodes.size() < stats.size()) { + if(checkSynchronizedCompleteness(head, setNodes)) { + res = new GeneralStatement(head, setNodes, same?null:post); + stat.collapseNodesToStatement(res); + + return res; + } + } + } + } + } + } + + return null; + } + + private static boolean checkSynchronizedCompleteness(Statement head, HashSet<Statement> setNodes) { + + // check exit nodes + for(Statement stat : setNodes) { + if(stat.isMonitorEnter()) { + List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); + if(lstSuccs.size() != 1 || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { + return false; + } + + if(!setNodes.contains(lstSuccs.get(0).getDestination())) { + return false; + } + } + } + + return true; + } + + private static boolean findSimpleStatements(Statement stat, HashMap<Integer, Set<Integer>> mapExtPost) { + + boolean found, success = false; + + do { + found = false; + + List<Statement> lstStats = stat.getPostReversePostOrderList(); + for(Statement st: lstStats) { + + Statement result = detectStatement(st); + + if(result != null) { + + if(stat.type == Statement.TYPE_GENERAL && result.getFirst() == stat.getFirst() && + stat.getStats().size() == result.getStats().size()) { + // mark general statement + stat.type = Statement.TYPE_PLACEHOLDER; + } + + stat.collapseNodesToStatement(result); + + // update the postdominator map + if(!mapExtPost.isEmpty()) { + HashSet<Integer> setOldNodes = new HashSet<Integer>(); + for(Statement old : result.getStats()) { + setOldNodes.add(old.id); + } + + Integer newid = result.id; + + for(Integer key : new ArrayList<Integer>(mapExtPost.keySet())) { + Set<Integer> set = mapExtPost.get(key); + + int oldsize = set.size(); + set.removeAll(setOldNodes); + + if(setOldNodes.contains(key)) { + Set<Integer> setNew = mapExtPost.get(newid); + if(setNew == null) { + mapExtPost.put(newid, setNew = new HashSet<Integer>()); + } + setNew.addAll(set); + + mapExtPost.remove(key); + } else { + if(set.size() < oldsize) { + set.add(newid); + } + } + } + } + + + found = true; + break; + } + } + + if(found) { + success = true; + } + + } while(found); + + return success; + } + + + private static Statement detectStatement(Statement head) { + + Statement res; + + if((res = DoStatement.isHead(head)) != null) { + return res; + } + + if((res = SwitchStatement.isHead(head)) != null) { + return res; + } + + if((res = IfStatement.isHead(head)) != null) { + return res; + } + + // synchronized statements will be identified later + // right now they are recognized as catchall + + if((res = SequenceStatement.isHead2Block(head)) != null) { + return res; + } + + if((res = CatchStatement.isHead(head)) != null) { + return res; + } + + if((res = CatchAllStatement.isHead(head)) != null) { + return res; + } + + return null; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java new file mode 100644 index 0000000..6854cd3 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java @@ -0,0 +1,214 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class EliminateLoopsHelper { + + +// public static boolean eliminateLoops(Statement root) { +// +// boolean ret = eliminateLoopsRec(root); +// +// if(ret) { +// SequenceHelper.condenseSequences(root); +// +// HashSet<Integer> setReorderedIfs = new HashSet<Integer>(); +// +// SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(false); +// while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, null)) { +// SequenceHelper.condenseSequences(root); +// } +// } +// +// return ret; +// } + + private static boolean eliminateLoopsRec(Statement stat) { + + for(Statement st: stat.getStats()) { + if(eliminateLoopsRec(st)) { + return true; + } + } + + if(stat.type == Statement.TYPE_DO && isLoopRedundant((DoStatement)stat)) { + return true; + } + + return false; + } + + private static boolean isLoopRedundant(DoStatement loop) { + + if(loop.getLooptype() != DoStatement.LOOP_DO) { + return false; + } + + // get parent loop if exists + Statement parentloop = loop.getParent(); + while(parentloop != null && parentloop.type != Statement.TYPE_DO) { + parentloop = parentloop.getParent(); + } + + if(parentloop == null || parentloop.getBasichead() != loop.getBasichead()) { + return false; + } + + // collect relevant break edges + List<StatEdge> lstBreakEdges = new ArrayList<StatEdge>(); + for(StatEdge edge: loop.getLabelEdges()) { + if(edge.getType() == StatEdge.TYPE_BREAK) { // all break edges are explicit because of LOOP_DO type + lstBreakEdges.add(edge); + } + } + + + Statement loopcontent = loop.getFirst(); + + boolean firstok = loopcontent.getAllSuccessorEdges().isEmpty(); + if(!firstok) { + StatEdge edge = loopcontent.getAllSuccessorEdges().get(0); + firstok = (edge.closure == loop && edge.getType() == StatEdge.TYPE_BREAK); + if(firstok) { + lstBreakEdges.remove(edge); + } + } + + + if(!lstBreakEdges.isEmpty()) { + if(firstok) { + + HashMap<Integer, Boolean> statLabeled = new HashMap<Integer, Boolean>(); + List<Statement> lstEdgeClosures = new ArrayList<Statement>(); + + for(StatEdge edge: lstBreakEdges) { + Statement minclosure = LowBreakHelper.getMinClosure(loopcontent, edge.getSource()); + lstEdgeClosures.add(minclosure); + } + + int precount = loop.isLabeled()?1:0; + for(Statement st: lstEdgeClosures) { + if(!statLabeled.containsKey(st.id)) { + boolean btemp = st.isLabeled(); + precount+=btemp?1:0; + statLabeled.put(st.id, btemp); + } + } + + for(int i=0;i<lstBreakEdges.size();i++) { + Statement st = lstEdgeClosures.get(i); + statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); + } + + for(int i=0;i<lstBreakEdges.size();i++) { + lstEdgeClosures.set(i, getMaxBreakLift(lstEdgeClosures.get(i), lstBreakEdges.get(i), statLabeled, loop)); + } + + statLabeled.clear(); + for(Statement st: lstEdgeClosures) { + statLabeled.put(st.id, st.isLabeled()); + } + + for(int i=0;i<lstBreakEdges.size();i++) { + Statement st = lstEdgeClosures.get(i); + statLabeled.put(st.id, LowBreakHelper.isBreakEdgeLabeled(lstBreakEdges.get(i).getSource(), st) | statLabeled.get(st.id)); + } + + int postcount = 0; + for(Boolean val: statLabeled.values()) { + postcount+=val?1:0; + } + + if(precount <= postcount) { + return false; + } else { + for(int i=0;i<lstBreakEdges.size();i++) { + lstEdgeClosures.get(i).addLabeledEdge(lstBreakEdges.get(i)); + } + } + + } else { + return false; + } + } + + eliminateLoop(loop, parentloop); + + return true; + } + + private static Statement getMaxBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) { + + Statement closure = stat; + Statement newclosure = stat; + + while((newclosure = getNextBreakLift(newclosure, edge, statLabeled, max)) != null) { + closure = newclosure; + } + + return closure; + } + + private static Statement getNextBreakLift(Statement stat, StatEdge edge, HashMap<Integer, Boolean> statLabeled, Statement max) { + + Statement closure = stat.getParent(); + + while(closure!=null && closure!=max && !closure.containsStatementStrict(edge.getDestination())) { + + boolean edge_labeled = LowBreakHelper.isBreakEdgeLabeled(edge.getSource(), closure); + boolean stat_labeled = statLabeled.containsKey(closure.id)?statLabeled.get(closure.id):closure.isLabeled(); + + if(stat_labeled || !edge_labeled) { + return closure; + } + + closure = closure.getParent(); + } + + return null; + } + + private static void eliminateLoop(Statement loop, Statement parentloop) { + + // move continue edges to the parent loop + List<StatEdge> lst = new ArrayList<StatEdge>(loop.getLabelEdges()); + for(StatEdge edge: lst) { + loop.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, parentloop); + parentloop.addPredecessor(edge); + + parentloop.addLabeledEdge(edge); + } + + // remove the last break edge, if exists + Statement loopcontent = loop.getFirst(); + if(!loopcontent.getAllSuccessorEdges().isEmpty()) { + loopcontent.removeSuccessor(loopcontent.getAllSuccessorEdges().get(0)); + } + + // replace loop with its content + loop.getParent().replaceStatement(loop, loopcontent); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java new file mode 100644 index 0000000..352415a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java @@ -0,0 +1,348 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; + +public class ExitHelper { + + + public static boolean condenseExits(RootStatement root) { + + int changed = integrateExits(root); + + if(changed > 0) { + + cleanUpUnreachableBlocks(root); + + SequenceHelper.condenseSequences(root); + } + + return (changed > 0); + } + + + private static void cleanUpUnreachableBlocks(Statement stat) { + + boolean found; + do { + + found = false; + + for(int i=0;i<stat.getStats().size();i++) { + + Statement st = stat.getStats().get(i); + + cleanUpUnreachableBlocks(st); + + if(st.type == Statement.TYPE_SEQUENCE && st.getStats().size() > 1) { + + Statement last = st.getStats().getLast(); + Statement secondlast = st.getStats().get(st.getStats().size()-2); + + if(last.getExprents() == null || !last.getExprents().isEmpty()) { + if(!secondlast.hasBasicSuccEdge()) { + + Set<Statement> set = last.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_BACKWARD); + set.remove(secondlast); + + if(set.isEmpty()) { + last.setExprents(new ArrayList<Exprent>()); + found = true; + break; + } + } + } + } + } + + } while(found); + + } + + + private static int integrateExits(Statement stat) { + + int ret = 0; + Statement dest = null; + + if(stat.getExprents() == null) { + + for(;;) { + + int changed = 0; + + for(Statement st: stat.getStats()) { + changed = integrateExits(st); + if(changed > 0) { + ret = 1; + break; + } + } + + if(changed == 0) { + break; + } + } + + + switch(stat.type) { + case Statement.TYPE_IF: + IfStatement ifst = (IfStatement)stat; + if(ifst.getIfstat() == null) { + StatEdge ifedge = ifst.getIfEdge(); + dest = isExitEdge(ifedge); + if(dest != null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); + + ifst.getFirst().removeSuccessor(ifedge); + StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, ifst.getFirst(), bstat); + ifst.getFirst().addSuccessor(newedge); + ifst.setIfEdge(newedge); + ifst.setIfstat(bstat); + ifst.getStats().addWithKey(bstat, bstat.id); + bstat.setParent(ifst); + + StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); + StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); + bstat.addSuccessor(newexitedge); + oldexitedge.closure.addLabeledEdge(newexitedge); + ret = 1; + } + } + } + } + + + if(stat.getAllSuccessorEdges().size() == 1 && stat.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_BREAK && stat.getLabelEdges().isEmpty()) { + Statement parent = stat.getParent(); + if(stat != parent.getFirst() || (parent.type != Statement.TYPE_IF && + parent.type != Statement.TYPE_SWITCH)) { + + StatEdge destedge = stat.getAllSuccessorEdges().get(0); + dest = isExitEdge(destedge); + if(dest != null) { + stat.removeSuccessor(destedge); + + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(DecHelper.copyExprentList(dest.getExprents())); + + StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0); + StatEdge newexitedge = new StatEdge(StatEdge.TYPE_BREAK, bstat, oldexitedge.getDestination()); + bstat.addSuccessor(newexitedge); + oldexitedge.closure.addLabeledEdge(newexitedge); + + SequenceStatement block = new SequenceStatement(Arrays.asList(new Statement[] {stat, bstat})); + block.setAllParent(); + + parent.replaceStatement(stat, block); + // LabelHelper.lowContinueLabels not applicable because of forward continue edges + // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); + // do it by hand + for(StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { + + block.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, stat); + stat.addPredecessor(prededge); + + stat.addLabeledEdge(prededge); + } + + + stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, bstat)); + + for(StatEdge edge : dest.getAllPredecessorEdges()) { + if(!edge.explicit && stat.containsStatementStrict(edge.getSource()) && + MergeHelper.isDirectPath(edge.getSource().getParent(), bstat)) { + + dest.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, bstat); + bstat.addPredecessor(edge); + + if(!stat.containsStatementStrict(edge.closure)) { + stat.addLabeledEdge(edge); + } + } + } + + ret = 2; + } + } + } + + return ret; + } + + private static Statement isExitEdge(StatEdge edge) { + + Statement dest = edge.getDestination(); + + if(edge.getType() == StatEdge.TYPE_BREAK && dest.type == Statement.TYPE_BASICBLOCK + && edge.explicit && (edge.labeled || isOnlyEdge(edge))) { + List<Exprent> data = dest.getExprents(); + + if(data != null && data.size() == 1) { + if(data.get(0).type == Exprent.EXPRENT_EXIT) { + return dest; + } + } + } + + return null; + } + + private static boolean isOnlyEdge(StatEdge edge) { + + Statement stat = edge.getDestination(); + + for(StatEdge ed: stat.getAllPredecessorEdges()) { + if(ed != edge) { + if(ed.getType() == StatEdge.TYPE_REGULAR) { + Statement source = ed.getSource(); + + if(source.type == Statement.TYPE_BASICBLOCK || (source.type == Statement.TYPE_IF && + ((IfStatement)source).iftype == IfStatement.IFTYPE_IF) || + (source.type == Statement.TYPE_DO && ((DoStatement)source).getLooptype() != DoStatement.LOOP_DO)) { + return false; + } + } else { + return false; + } + } + } + + return true; + } + + public static boolean removeRedundantReturns(RootStatement root) { + + boolean res = false; + + for(StatEdge edge: root.getDummyExit().getAllPredecessorEdges()) { + if(!edge.explicit) { + Statement source = edge.getSource(); + List<Exprent> lstExpr = source.getExprents(); + if(lstExpr != null && !lstExpr.isEmpty()) { + Exprent expr = lstExpr.get(lstExpr.size() - 1); + if(expr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ex = (ExitExprent)expr; + if(ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { + // remove redundant return + lstExpr.remove(lstExpr.size() - 1); + res = true; + } + } + } + } + } + + return res; + } + + public static boolean handleReturnFromInitializer(RootStatement root) { + + boolean res = false; + + Statement exit = root.getDummyExit(); + Statement top = root.getFirst(); + Statement newret = null; + + boolean sharedcreated = false; + + for(StatEdge edge: exit.getAllPredecessorEdges()) { + if(edge.explicit) { + + if(!sharedcreated) { + newret = addSharedInitializerReturn(root); + sharedcreated = true; + } + + Statement source = edge.getSource(); + List<Exprent> lstExpr = source.getExprents(); + if(lstExpr != null && !lstExpr.isEmpty()) { + Exprent expr = lstExpr.get(lstExpr.size() - 1); + if(expr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ex = (ExitExprent)expr; + if(ex.getExittype() == ExitExprent.EXIT_RETURN && ex.getValue() == null) { + lstExpr.remove(lstExpr.size() - 1); + + source.removeSuccessor(edge); + source.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, source, newret, top)); + + res = true; + } + } + } + } + } + + return res; + } + + private static Statement addSharedInitializerReturn(RootStatement root) { + + Statement exit = root.getDummyExit(); + Statement top = root.getFirst(); + + // build a new statement with the single instruction 'return' + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + + ExitExprent retexpr = new ExitExprent(ExitExprent.EXIT_RETURN, null, + ((MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret); + // a changeable list needed + bstat.setExprents(new ArrayList<Exprent>(Arrays.asList(new Exprent[]{retexpr}))); + + // build sequence to replace the former top statement + SequenceStatement seq = new SequenceStatement(Arrays.asList(new Statement[]{top, bstat})); + top.setParent(seq); + bstat.setParent(seq); + seq.setParent(root); + + root.getStats().removeWithKey(top.id); + root.getStats().addWithKeyAndIndex(0, seq, seq.id); + root.setFirst(seq); + + for(StatEdge succedge : top.getAllSuccessorEdges()) { + top.removeSuccessor(succedge); + } + + top.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, top, bstat)); + bstat.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, bstat, exit, seq)); + + return bstat; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java new file mode 100644 index 0000000..362e1a2 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java @@ -0,0 +1,886 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.attr.StructBootstrapMethodsAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; +import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.struct.consts.PooledConstant; +import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class ExprProcessor implements CodeConstants { + + public static final String UNDEFINED_TYPE_STRING = "<undefinedtype>"; + public static final String UNKNOWN_TYPE_STRING = "<unknown>"; + public static final String NULL_TYPE_STRING = "<null>"; + + private static final HashMap<Integer, Integer> mapConsts = new HashMap<Integer, Integer>(); + + static { + + // mapConsts.put(new Integer(opc_i2l), new + // Integer(FunctionExprent.FUNCTION_I2L)); + // mapConsts.put(new Integer(opc_i2f), new + // Integer(FunctionExprent.FUNCTION_I2F)); + // mapConsts.put(new Integer(opc_i2d), new + // Integer(FunctionExprent.FUNCTION_I2D)); + // mapConsts.put(new Integer(opc_l2i), new + // Integer(FunctionExprent.FUNCTION_L2I)); + // mapConsts.put(new Integer(opc_l2f), new + // Integer(FunctionExprent.FUNCTION_L2F)); + // mapConsts.put(new Integer(opc_l2d), new + // Integer(FunctionExprent.FUNCTION_L2D)); + // mapConsts.put(new Integer(opc_f2i), new + // Integer(FunctionExprent.FUNCTION_F2I)); + // mapConsts.put(new Integer(opc_f2l), new + // Integer(FunctionExprent.FUNCTION_F2L)); + // mapConsts.put(new Integer(opc_f2d), new + // Integer(FunctionExprent.FUNCTION_F2D)); + // mapConsts.put(new Integer(opc_d2i), new + // Integer(FunctionExprent.FUNCTION_D2I)); + // mapConsts.put(new Integer(opc_d2l), new + // Integer(FunctionExprent.FUNCTION_D2L)); + // mapConsts.put(new Integer(opc_d2f), new + // Integer(FunctionExprent.FUNCTION_D2F)); + // mapConsts.put(new Integer(opc_i2b), new + // Integer(FunctionExprent.FUNCTION_I2B)); + // mapConsts.put(new Integer(opc_i2c), new + // Integer(FunctionExprent.FUNCTION_I2C)); + // mapConsts.put(new Integer(opc_i2s), new + // Integer(FunctionExprent.FUNCTION_I2S)); + + mapConsts.put(new Integer(opc_arraylength), new Integer(FunctionExprent.FUNCTION_ARRAYLENGTH)); + mapConsts.put(new Integer(opc_checkcast), new Integer(FunctionExprent.FUNCTION_CAST)); + mapConsts.put(new Integer(opc_instanceof), new Integer(FunctionExprent.FUNCTION_INSTANCEOF)); + + } + + private static final VarType[] consts = new VarType[] { VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, + VarType.VARTYPE_STRING }; + + private static final VarType[] vartypes = new VarType[] { VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT }; + + private static final VarType[] arrtypes = new VarType[] { VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT, + VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT }; + + private static final int[] func1 = new int[] { FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV, + FunctionExprent.FUNCTION_REM }; + + private static final int[] func2 = new int[] { FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND, + FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR }; + + private static final int[] func3 = new int[] { FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I, + FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L, FunctionExprent.FUNCTION_F2D, + FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F, FunctionExprent.FUNCTION_I2B, FunctionExprent.FUNCTION_I2C, + FunctionExprent.FUNCTION_I2S }; + + private static final int[] func4 = new int[] { FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL, + FunctionExprent.FUNCTION_DCMPG }; + + private static final int[] func5 = new int[] { IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE }; + + private static final int[] func6 = new int[] { IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE, + IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE }; + + private static final int[] func7 = new int[] { IfExprent.IF_NULL, IfExprent.IF_NONNULL }; + + private static final int[] func8 = new int[] { MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT }; + + private static final int[] arr_type = new int[] { CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE, + CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG }; + + private static final int[] negifs = new int[] { IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL, + IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE, IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, + IfExprent.IF_ACMPEQ }; + + private static final String[] typeNames = new String[] { "byte", "char", "double", "float", "int", "long", "short", "boolean", }; + + private VarProcessor varProcessor = (VarProcessor) DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); + + public void processStatement(RootStatement root, StructClass cl) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + +// try { +// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); +// } catch (Exception ex) { +// ex.printStackTrace(); +// } + + // collect finally entry points + Set<String> setFinallyShortRangeEntryPoints = new HashSet<String>(); + for (List<FinallyPathWrapper> lst : dgraph.mapShortRangeFinallyPaths.values()) { + for (FinallyPathWrapper finwrap : lst) { + setFinallyShortRangeEntryPoints.add(finwrap.entry); + } + } + + Set<String> setFinallyLongRangeEntryPaths = new HashSet<String>(); + for (List<FinallyPathWrapper> lst : dgraph.mapLongRangeFinallyPaths.values()) { + for (FinallyPathWrapper finwrap : lst) { + setFinallyLongRangeEntryPaths.add(finwrap.source + "##" + finwrap.entry); + } + } + + Map<String, VarExprent> mapCatch = new HashMap<String, VarExprent>(); + collectCatchVars(root, flatthelper, mapCatch); + + Map<DirectNode, Map<String, PrimitiveExprsList>> mapData = new HashMap<DirectNode, Map<String, PrimitiveExprsList>>(); + + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<LinkedList<String>>(); + + stack.add(dgraph.first); + stackEntryPoint.add(new LinkedList<String>()); + + Map<String, PrimitiveExprsList> map = new HashMap<String, PrimitiveExprsList>(); + map.put(null, new PrimitiveExprsList()); + mapData.put(dgraph.first, map); + + while (!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + LinkedList<String> entrypoints = stackEntryPoint.removeFirst(); + + PrimitiveExprsList data; + if (mapCatch.containsKey(node.id)) { + data = getExpressionData(mapCatch.get(node.id)); + } else { + data = mapData.get(node).get(buildEntryPointKey(entrypoints)); + } + + BasicBlockStatement block = node.block; + if (block != null) { + processBlock(block, data, cl); + block.setExprents(data.getLstExprents()); + } + + String currentEntrypoint = entrypoints.isEmpty() ? null : entrypoints.getLast(); + + for (DirectNode nd : node.succs) { + + boolean isSuccessor = true; + if (currentEntrypoint != null && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) { + isSuccessor = false; + for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(node.id)) { + if (finwraplong.source.equals(currentEntrypoint) && finwraplong.destination.equals(nd.id)) { + isSuccessor = true; + break; + } + } + } + + if (isSuccessor) { + + Map<String, PrimitiveExprsList> mapSucc = mapData.get(nd); + if (mapSucc == null) { + mapData.put(nd, mapSucc = new HashMap<String, PrimitiveExprsList>()); + } + + LinkedList<String> ndentrypoints = new LinkedList<String>(entrypoints); + + if (setFinallyLongRangeEntryPaths.contains(node.id + "##" + nd.id)) { + ndentrypoints.addLast(node.id); + } else if (!setFinallyShortRangeEntryPoints.contains(nd.id) && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) { + ndentrypoints.removeLast(); // currentEntrypoint should + // not be null at this point + } + + // handling of entry point loops + int succ_entry_index = ndentrypoints.indexOf(nd.id); + if(succ_entry_index >= 0) { // we are in a loop (e.g. continue in a finally block), drop all entry points in the list beginning with succ_entry_index + for(int elements_to_remove = ndentrypoints.size() - succ_entry_index; elements_to_remove > 0; elements_to_remove--) { + ndentrypoints.removeLast(); + } + } + + String ndentrykey = buildEntryPointKey(ndentrypoints); + if (!mapSucc.containsKey(ndentrykey)) { + + mapSucc.put(ndentrykey, copyVarExprents(data.copyStack())); + + stack.add(nd); + stackEntryPoint.add(ndentrypoints); + } + } + } + } + + initStatementExprents(root); + } + + // FIXME: Ugly code, to be rewritten. A tuple class is needed. + private String buildEntryPointKey(LinkedList<String> entrypoints) { + if (entrypoints.isEmpty()) { + return null; + } else { + StringBuilder buffer = new StringBuilder(); + for (String point : entrypoints) { + buffer.append(point); + buffer.append(":"); + } + return buffer.toString(); + } + } + + private PrimitiveExprsList copyVarExprents(PrimitiveExprsList data) { + ExprentStack stack = data.getStack(); + for (int i = 0; i < stack.size(); i++) { + stack.set(i, stack.get(i).copy()); + } + return data; + } + + private void collectCatchVars(Statement stat, FlattenStatementsHelper flatthelper, Map<String, VarExprent> map) { + + List<VarExprent> lst = null; + + if (stat.type == Statement.TYPE_CATCHALL) { + CatchAllStatement catchall = (CatchAllStatement) stat; + if (!catchall.isFinally()) { + lst = catchall.getVars(); + } + } else if (stat.type == Statement.TYPE_TRYCATCH) { + lst = ((CatchStatement) stat).getVars(); + } + + if (lst != null) { + for (int i = 1; i < stat.getStats().size(); i++) { + map.put(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0], lst.get(i - 1)); + } + } + + for (Statement st : stat.getStats()) { + collectCatchVars(st, flatthelper, map); + } + } + + private void initStatementExprents(Statement stat) { + stat.initExprents(); + + for (Statement st : stat.getStats()) { + initStatementExprents(st); + } + } + + public void processBlock(BasicBlockStatement stat, PrimitiveExprsList data, StructClass cl) { + + ConstantPool pool = cl.getPool(); + StructBootstrapMethodsAttribute bootstrap = (StructBootstrapMethodsAttribute)cl.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS); + + BasicBlock block = stat.getBlock(); + + ExprentStack stack = data.getStack(); + List<Exprent> exprlist = data.getLstExprents(); + + InstructionSequence seq = block.getSeq(); + + for (int i = 0; i < seq.length(); i++) { + + Instruction instr = seq.getInstr(i); + + switch (instr.opcode) { + case opc_aconst_null: + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_NULL, null)); + break; + case opc_bipush: + case opc_sipush: + pushEx(stack, exprlist, new ConstExprent(instr.getOperand(0), true)); + break; + case opc_lconst_0: + case opc_lconst_1: + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_LONG, new Long(instr.opcode - opc_lconst_0))); + break; + case opc_fconst_0: + case opc_fconst_1: + case opc_fconst_2: + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_FLOAT, new Float(instr.opcode - opc_fconst_0))); + break; + case opc_dconst_0: + case opc_dconst_1: + pushEx(stack, exprlist, new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(instr.opcode - opc_dconst_0))); + break; + case opc_ldc: + case opc_ldc_w: + case opc_ldc2_w: + PrimitiveConstant cn = pool.getPrimitiveConstant(instr.getOperand(0)); + pushEx(stack, exprlist, new ConstExprent(consts[cn.type - CONSTANT_Integer], cn.value)); + break; + case opc_iload: + case opc_lload: + case opc_fload: + case opc_dload: + case opc_aload: + pushEx(stack, exprlist, new VarExprent(instr.getOperand(0), vartypes[instr.opcode - opc_iload], varProcessor)); + break; + case opc_iaload: + case opc_laload: + case opc_faload: + case opc_daload: + case opc_aaload: + case opc_baload: + case opc_caload: + case opc_saload: + Exprent index = stack.pop(); + Exprent arr = stack.pop(); + + VarType vartype = null; + switch (instr.opcode) { + case opc_laload: + vartype = VarType.VARTYPE_LONG; + break; + case opc_daload: + vartype = VarType.VARTYPE_DOUBLE; + } + pushEx(stack, exprlist, new ArrayExprent(arr, index, arrtypes[instr.opcode - opc_iaload]), vartype); + break; + case opc_istore: + case opc_lstore: + case opc_fstore: + case opc_dstore: + case opc_astore: + Exprent top = stack.pop(); + int varindex = instr.getOperand(0); + AssignmentExprent assign = new AssignmentExprent(new VarExprent(varindex, vartypes[instr.opcode - opc_istore], varProcessor), top); + exprlist.add(assign); + break; + case opc_iastore: + case opc_lastore: + case opc_fastore: + case opc_dastore: + case opc_aastore: + case opc_bastore: + case opc_castore: + case opc_sastore: + Exprent value = stack.pop(); + Exprent index_store = stack.pop(); + Exprent arr_store = stack.pop(); + AssignmentExprent arrassign = new AssignmentExprent(new ArrayExprent(arr_store, index_store, arrtypes[instr.opcode - opc_iastore]), value); + exprlist.add(arrassign); + break; + case opc_iadd: + case opc_ladd: + case opc_fadd: + case opc_dadd: + case opc_isub: + case opc_lsub: + case opc_fsub: + case opc_dsub: + case opc_imul: + case opc_lmul: + case opc_fmul: + case opc_dmul: + case opc_idiv: + case opc_ldiv: + case opc_fdiv: + case opc_ddiv: + case opc_irem: + case opc_lrem: + case opc_frem: + case opc_drem: + pushEx(stack, exprlist, new FunctionExprent(func1[(instr.opcode - opc_iadd) / 4], stack)); + break; + case opc_ishl: + case opc_lshl: + case opc_ishr: + case opc_lshr: + case opc_iushr: + case opc_lushr: + case opc_iand: + case opc_land: + case opc_ior: + case opc_lor: + case opc_ixor: + case opc_lxor: + pushEx(stack, exprlist, new FunctionExprent(func2[(instr.opcode - opc_ishl) / 2], stack)); + break; + case opc_ineg: + case opc_lneg: + case opc_fneg: + case opc_dneg: + pushEx(stack, exprlist, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack)); + break; + case opc_iinc: + VarExprent vevar = new VarExprent(instr.getOperand(0), VarType.VARTYPE_INT, varProcessor); + exprlist.add(new AssignmentExprent(vevar, new FunctionExprent(instr.getOperand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD, Arrays + .asList(new Exprent[] { vevar.copy(), new ConstExprent(VarType.VARTYPE_INT, new Integer(Math.abs(instr.getOperand(1)))) })))); + break; + case opc_i2l: + case opc_i2f: + case opc_i2d: + case opc_l2i: + case opc_l2f: + case opc_l2d: + case opc_f2i: + case opc_f2l: + case opc_f2d: + case opc_d2i: + case opc_d2l: + case opc_d2f: + case opc_i2b: + case opc_i2c: + case opc_i2s: + pushEx(stack, exprlist, new FunctionExprent(func3[instr.opcode - opc_i2l], stack)); + break; + case opc_lcmp: + case opc_fcmpl: + case opc_fcmpg: + case opc_dcmpl: + case opc_dcmpg: + pushEx(stack, exprlist, new FunctionExprent(func4[instr.opcode - opc_lcmp], stack)); + break; + case opc_ifeq: + case opc_ifne: + case opc_iflt: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + exprlist.add(new IfExprent(negifs[func5[instr.opcode - opc_ifeq]], stack)); + break; + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmplt: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_acmpeq: + case opc_if_acmpne: + exprlist.add(new IfExprent(negifs[func6[instr.opcode - opc_if_icmpeq]], stack)); + break; + case opc_ifnull: + case opc_ifnonnull: + exprlist.add(new IfExprent(negifs[func7[instr.opcode - opc_ifnull]], stack)); + break; + case opc_tableswitch: + case opc_lookupswitch: + exprlist.add(new SwitchExprent(stack.pop())); + break; + case opc_ireturn: + case opc_lreturn: + case opc_freturn: + case opc_dreturn: + case opc_areturn: + case opc_return: + case opc_athrow: + exprlist.add(new ExitExprent(instr.opcode == opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN, instr.opcode == opc_return ? null : stack.pop(), + instr.opcode == opc_athrow ? null : ((MethodDescriptor) DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR)).ret)); + break; + case opc_monitorenter: + case opc_monitorexit: + exprlist.add(new MonitorExprent(func8[instr.opcode - opc_monitorenter], stack.pop())); + break; + case opc_checkcast: + case opc_instanceof: + stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true), null)); + case opc_arraylength: + pushEx(stack, exprlist, new FunctionExprent(mapConsts.get(instr.opcode).intValue(), stack)); + break; + case opc_getstatic: + case opc_getfield: + pushEx(stack, exprlist, new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_getstatic ? null : stack.pop())); + break; + case opc_putstatic: + case opc_putfield: + Exprent valfield = stack.pop(); + Exprent exprfield = new FieldExprent(pool.getLinkConstant(instr.getOperand(0)), instr.opcode == opc_putstatic ? null : stack.pop()); + exprlist.add(new AssignmentExprent(exprfield, valfield)); + break; + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_invokedynamic: + if(instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) { + + LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0)); + int dynamic_invokation_type = -1; + + if(instr.opcode == opc_invokedynamic && bootstrap != null) { + List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1); + LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1); + + dynamic_invokation_type = content_method_handle.index1; + } + + InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type); + if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) { + exprlist.add(exprinv); + } else { + pushEx(stack, exprlist, exprinv); + } + } + break; + case opc_new: + case opc_anewarray: + case opc_multianewarray: + int arrdims = (instr.opcode == opc_new) ? 0 : (instr.opcode == opc_anewarray) ? 1 : instr.getOperand(1); + VarType arrtype = new VarType(pool.getPrimitiveConstant(instr.getOperand(0)).getString(), true); + if (instr.opcode != opc_multianewarray) { + arrtype.arraydim += arrdims; + } + pushEx(stack, exprlist, new NewExprent(arrtype, stack, arrdims)); + break; + case opc_newarray: + pushEx(stack, exprlist, new NewExprent(new VarType(arr_type[instr.getOperand(0) - 4], 1), stack, 1)); + break; + case opc_dup: + pushEx(stack, exprlist, stack.getByOffset(-1).copy()); + break; + case opc_dup_x1: + insertByOffsetEx(-2, stack, exprlist, -1); + break; + case opc_dup_x2: + if (stack.getByOffset(-2).getExprType().stack_size == 2) { + insertByOffsetEx(-2, stack, exprlist, -1); + } else { + insertByOffsetEx(-3, stack, exprlist, -1); + } + break; + case opc_dup2: + if (stack.getByOffset(-1).getExprType().stack_size == 2) { + pushEx(stack, exprlist, stack.getByOffset(-1).copy()); + } else { + pushEx(stack, exprlist, stack.getByOffset(-2).copy()); + pushEx(stack, exprlist, stack.getByOffset(-2).copy()); + } + break; + case opc_dup2_x1: + if (stack.getByOffset(-1).getExprType().stack_size == 2) { + insertByOffsetEx(-2, stack, exprlist, -1); + } else { + insertByOffsetEx(-3, stack, exprlist, -2); + insertByOffsetEx(-3, stack, exprlist, -1); + } + break; + case opc_dup2_x2: + if (stack.getByOffset(-1).getExprType().stack_size == 2) { + if (stack.getByOffset(-2).getExprType().stack_size == 2) { + insertByOffsetEx(-2, stack, exprlist, -1); + } else { + insertByOffsetEx(-3, stack, exprlist, -1); + } + } else { + if (stack.getByOffset(-3).getExprType().stack_size == 2) { + insertByOffsetEx(-3, stack, exprlist, -2); + insertByOffsetEx(-3, stack, exprlist, -1); + } else { + insertByOffsetEx(-4, stack, exprlist, -2); + insertByOffsetEx(-4, stack, exprlist, -1); + } + } + break; + case opc_swap: + insertByOffsetEx(-2, stack, exprlist, -1); + stack.pop(); + break; + case opc_pop: + case opc_pop2: + stack.pop(); + } + + } + + } + + private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent) { + pushEx(stack, exprlist, exprent, null); + } + + private void pushEx(ExprentStack stack, List<Exprent> exprlist, Exprent exprent, VarType vartype) { + int varindex = VarExprent.STACK_BASE + stack.size(); + VarExprent var = new VarExprent(varindex, vartype == null ? exprent.getExprType() : vartype, varProcessor); + var.setStack(true); + + exprlist.add(new AssignmentExprent(var, exprent)); + stack.push(var.copy()); + } + + private void insertByOffsetEx(int offset, ExprentStack stack, List<Exprent> exprlist, int copyoffset) { + + int base = VarExprent.STACK_BASE + stack.size(); + + LinkedList<VarExprent> lst = new LinkedList<VarExprent>(); + + for (int i = -1; i >= offset; i--) { + Exprent varex = stack.pop(); + VarExprent varnew = new VarExprent(base + i + 1, varex.getExprType(), varProcessor); + varnew.setStack(true); + exprlist.add(new AssignmentExprent(varnew, varex)); + lst.add(0, (VarExprent) varnew.copy()); + } + + Exprent exprent = lst.get(lst.size() + copyoffset).copy(); + VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor); + var.setStack(true); + exprlist.add(new AssignmentExprent(var, exprent)); + lst.add(0, (VarExprent) var.copy()); + + for (VarExprent expr : lst) { + stack.push(expr); + } + + } + + public static String getTypeName(VarType type) { + return getTypeName(type, true); + } + + public static String getTypeName(VarType type, boolean getShort) { + + int tp = type.type; + if (tp <= CodeConstants.TYPE_BOOLEAN) { + return typeNames[tp]; + } else if (tp == CodeConstants.TYPE_UNKNOWN) { + return UNKNOWN_TYPE_STRING; // INFO: should not occur + } else if (tp == CodeConstants.TYPE_NULL) { + return NULL_TYPE_STRING; // INFO: should not occur + } else if (tp == CodeConstants.TYPE_VOID) { + return "void"; + } else if (tp == CodeConstants.TYPE_OBJECT) { + String ret = ExprProcessor.buildJavaClassName(type.value); + if (getShort) { + ret = DecompilerContext.getImpcollector().getShortName(ret); + } + + if (ret == null) { + // FIXME: a warning should be logged + ret = UNDEFINED_TYPE_STRING; + } + return ret; + } + + throw new RuntimeException("invalid type"); + } + + public static String getCastTypeName(VarType type) { + return getCastTypeName(type, true); + } + + public static String getCastTypeName(VarType type, boolean getShort) { + String s = getTypeName(type, getShort); + int dim = type.arraydim; + while (dim-- > 0) { + s += "[]"; + } + return s; + } + + public static PrimitiveExprsList getExpressionData(VarExprent var) { + PrimitiveExprsList prlst = new PrimitiveExprsList(); + VarExprent vartmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor()); + vartmp.setStack(true); + + prlst.getLstExprents().add(new AssignmentExprent(vartmp, var.copy())); + prlst.getStack().push(vartmp.copy()); + return prlst; + } + + public static boolean endsWithSemikolon(Exprent expr) { + int type = expr.type; + return !(type == Exprent.EXPRENT_SWITCH || type == Exprent.EXPRENT_MONITOR || type == Exprent.EXPRENT_IF || (type == Exprent.EXPRENT_VAR && ((VarExprent) expr) + .isClassdef())); + } + + public static String jmpWrapper(Statement stat, int indent, boolean semicolon) { + StringBuffer buf = new StringBuffer(stat.toJava(indent)); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + List<StatEdge> lstSuccs = stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL); + if (lstSuccs.size() == 1) { + StatEdge edge = lstSuccs.get(0); + if (edge.getType() != StatEdge.TYPE_REGULAR && edge.explicit == true && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { + buf.append(InterpreterUtil.getIndentString(indent)); + + switch (edge.getType()) { + case StatEdge.TYPE_BREAK: + buf.append("break"); + break; + case StatEdge.TYPE_CONTINUE: + buf.append("continue"); + } + + if (edge.labeled) { + buf.append(" label" + edge.closure.id); + } + buf.append(";" + new_line_separator); + } + } + + if (buf.length() == 0 && semicolon) { + buf.append(InterpreterUtil.getIndentString(indent) + ";" + new_line_separator); + } + + return buf.toString(); + } + + public static String buildJavaClassName(String name) { + String res = name.replace('/', '.'); + + if (res.indexOf("$") >= 0) { // attempt to invoke foreign member + // classes correctly + StructClass cl = DecompilerContext.getStructcontext().getClass(name); + if (cl == null || !cl.isOwn()) { + res = res.replace('$', '.'); + } + } + + return res; + } + + public static String listToJava(List<Exprent> lst, int indent) { + if (lst == null || lst.isEmpty()) { + return ""; + } + + String indstr = InterpreterUtil.getIndentString(indent); + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + for (Exprent expr : lst) { + String content = expr.toJava(indent); + if (content.length() > 0) { + if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent) expr).isClassdef()) { + buf.append(indstr); + } + buf.append(content); + if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent) expr).getMontype() == MonitorExprent.MONITOR_ENTER) { + buf.append("{}"); // empty synchronized block + } + if (ExprProcessor.endsWithSemikolon(expr)) { + buf.append(";"); + } + buf.append(new_line_separator); + } + } + + return buf.toString(); + } + + public static ConstExprent getDefaultArrayValue(VarType arrtype) { + + ConstExprent defaultval; + if (arrtype.type == CodeConstants.TYPE_OBJECT || arrtype.arraydim > 0) { + defaultval = new ConstExprent(VarType.VARTYPE_NULL, null); + } else if (arrtype.type == CodeConstants.TYPE_FLOAT) { + defaultval = new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); + } else if (arrtype.type == CodeConstants.TYPE_LONG) { + defaultval = new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); + } else if (arrtype.type == CodeConstants.TYPE_DOUBLE) { + defaultval = new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); + } else { // integer types + defaultval = new ConstExprent(0, true); + } + + return defaultval; + } + + public static boolean getCastedExprent(Exprent exprent, VarType leftType, StringBuilder buffer, int indent, boolean castNull) { + return getCastedExprent(exprent, leftType, buffer, indent, castNull, false); + } + + public static boolean getCastedExprent(Exprent exprent, VarType leftType, StringBuilder buffer, int indent, boolean castNull, boolean castAlways) { + + boolean ret = false; + VarType rightType = exprent.getExprType(); + + String res = exprent.toJava(indent); + + boolean cast = !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT); + cast |= castAlways; + + if (!cast && castNull && rightType.type == CodeConstants.TYPE_NULL) { + // check for a nameless anonymous class + cast = !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType)); + } + if (!cast) { + cast = isIntConstant(exprent) && VarType.VARTYPE_INT.isStrictSuperset(leftType); + } + + if (cast) { + if (exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { + res = "(" + res + ")"; + } + + res = "(" + ExprProcessor.getCastTypeName(leftType) + ")" + res; + ret = true; + } + + buffer.append(res); + + return ret; + } + + private static boolean isIntConstant(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent) exprent; + switch (cexpr.getConsttype().type) { + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + return true; + } + } + + return false; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java new file mode 100644 index 0000000..468dc41 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java @@ -0,0 +1,46 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.util.ListStack; + +public class ExprentStack extends ListStack<Exprent> { + + public ExprentStack() {} + + public ExprentStack(ListStack<Exprent> list) { + super(list); + pointer = list.getPointer(); + } + + public Exprent push(Exprent item) { + super.push(item); + + return item; + } + + public Exprent pop() { + + Exprent o = this.remove(--pointer); + + return o; + } + + public ExprentStack clone() { + return new ExprentStack(this); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java new file mode 100644 index 0000000..3631090 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java @@ -0,0 +1,1031 @@ +/* + * 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 org.jetbrains.java.decompiler.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 org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.ConstantsUtil; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; +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.collectors.CounterContainer; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.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()); + + 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); + } + } + } + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java new file mode 100644 index 0000000..d11753e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java @@ -0,0 +1,320 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute; +import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +public class IdeaNotNullHelper { + + + public static boolean removeHardcodedChecks(Statement root, StructMethod mt) { + + boolean checks_removed = false; + + // parameter @NotNull annotations + while(findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check. + checks_removed = true; + } + + // method @NotNull annotation + while(findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check. + checks_removed = true; + } + + return checks_removed; + } + + private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) { + + Statement st = stat.getFirst(); + while(st.type == Statement.TYPE_SEQUENCE) { + st = st.getFirst(); + } + + if(st.type == Statement.TYPE_IF) { + + IfStatement ifstat = (IfStatement)st; + Statement ifbranch = ifstat.getIfstat(); + + Exprent if_condition = ifstat.getHeadexprent().getCondition(); + + boolean is_notnull_check = false; + + // TODO: FUNCTION_NE also possible if reversed order (in theory) + if(ifbranch != null && if_condition.type == Exprent.EXPRENT_FUNCTION && ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ && + ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { + + FunctionExprent func = (FunctionExprent)if_condition; + Exprent first_param = func.getLstOperands().get(0); + Exprent second_param = func.getLstOperands().get(1); + + if(second_param.type == Exprent.EXPRENT_CONST && ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order + if(first_param.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)first_param; + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes(); + + // parameter annotations + StructAnnotationParameterAttribute param_annotations = (StructAnnotationParameterAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); + if(param_annotations != null) { + + List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations(); + int method_param_number = md.params.length; + + int index = thisvar ? 1 : 0; + for (int i = 0; i < method_param_number; i++) { + + if(index == var.getIndex()) { + if(param_annotations_lists.size() >= method_param_number - i) { + int shift = method_param_number - param_annotations_lists.size(); // NOTE: workaround for compiler bug, count annotations starting with the last parameter + + List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift); + + for(AnnotationExprent ann : annotations) { + if(ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { + is_notnull_check = true; + } + } + } + + break; + } + + index+=md.params[i].stack_size; + } + } + } + } + } + + if(!is_notnull_check) { + return false; + } + + removeParameterCheck(stat, mt); + + return true; + } + + return false; + } + + private static void removeParameterCheck(Statement stat, StructMethod mt) { + + Statement st = stat.getFirst(); + while(st.type == Statement.TYPE_SEQUENCE) { + st = st.getFirst(); + } + + IfStatement ifstat = (IfStatement)st; + + if(ifstat.getElsestat() == null) { // if + // TODO: + } else { // if - else + + StatEdge ifedge = ifstat.getIfEdge(); + StatEdge elseedge = ifstat.getElseEdge(); + + Statement ifbranch = ifstat.getIfstat(); + Statement elsebranch = ifstat.getElsestat(); + + ifstat.getFirst().removeSuccessor(ifedge); + ifstat.getFirst().removeSuccessor(elseedge); + + ifstat.getStats().removeWithKey(ifbranch.id); + ifstat.getStats().removeWithKey(elsebranch.id); + + if(!ifbranch.getAllSuccessorEdges().isEmpty()) { + ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0)); + } + + ifstat.getParent().replaceStatement(ifstat, elsebranch); + ifstat.getParent().setAllParent(); + } + + } + + private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) { + + VBStyleCollection<StructGeneralAttribute, String> attributes = mt.getAttributes(); + + boolean is_notnull_check = false; + + // method annotation, refers to the return value + StructAnnotationAttribute attr = (StructAnnotationAttribute)attributes.getWithKey(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS); + if(attr != null) { + List<AnnotationExprent> annotations = attr.getAnnotations(); + + for(AnnotationExprent ann : annotations) { + if(ann.getClassname().equals("org/jetbrains/annotations/NotNull")) { + is_notnull_check = true; + } + } + } + + if(is_notnull_check) { + return removeReturnCheck(stat, mt); + } + + return false; + } + + + private static boolean removeReturnCheck(Statement stat, StructMethod mt) { + + Statement parent = stat.getParent(); + + if(parent != null && parent.type == Statement.TYPE_IF && stat.type == Statement.TYPE_BASICBLOCK && stat.getExprents().size() == 1) { + Exprent exprent = stat.getExprents().get(0); + if(exprent.type == Exprent.EXPRENT_EXIT) { + ExitExprent exit_exprent = (ExitExprent)exprent; + if(exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { + Exprent exprent_value = exit_exprent.getValue(); + //if(exprent_value.type == Exprent.EXPRENT_VAR) { + // VarExprent var_value = (VarExprent)exprent_value; + + IfStatement ifparent = (IfStatement)parent; + Exprent if_condition = ifparent.getHeadexprent().getCondition(); + + if(ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) + + FunctionExprent func = (FunctionExprent)if_condition; + Exprent first_param = func.getLstOperands().get(0); + Exprent second_param = func.getLstOperands().get(1); + + StatEdge ifedge = ifparent.getIfEdge(); + StatEdge elseedge = ifparent.getElseEdge(); + + Statement ifbranch = ifparent.getIfstat(); + Statement elsebranch = ifparent.getElsestat(); + + if(second_param.type == Exprent.EXPRENT_CONST && ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order + //if(first_param.type == Exprent.EXPRENT_VAR && ((VarExprent)first_param).getIndex() == var_value.getIndex()) { + if(first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc. + if(ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && // TODO: special check for IllegalStateException + ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { + + ifparent.getFirst().removeSuccessor(ifedge); + ifparent.getFirst().removeSuccessor(elseedge); + + ifparent.getStats().removeWithKey(ifbranch.id); + ifparent.getStats().removeWithKey(elsebranch.id); + + if(!ifbranch.getAllSuccessorEdges().isEmpty()) { + ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0)); + } + + if(!ifparent.getFirst().getExprents().isEmpty()) { + elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents()); + } + + ifparent.getParent().replaceStatement(ifparent, elsebranch); + ifparent.getParent().setAllParent(); + + return true; + } + } + } + } + //} + } + } + } else if (parent != null && parent.type == Statement.TYPE_SEQUENCE && stat.type == Statement.TYPE_BASICBLOCK && stat.getExprents().size() == 1) { + Exprent exprent = stat.getExprents().get(0); + if(exprent.type == Exprent.EXPRENT_EXIT) { + ExitExprent exit_exprent = (ExitExprent)exprent; + if(exit_exprent.getExittype() == ExitExprent.EXIT_RETURN) { + Exprent exprent_value = exit_exprent.getValue(); + + SequenceStatement sequence = (SequenceStatement)parent; + int sequence_stats_number = sequence.getStats().size(); + + if(sequence_stats_number > 1 && sequence.getStats().getLast() == stat && sequence.getStats().get(sequence_stats_number - 2).type == Statement.TYPE_IF) { + + IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2); + Exprent if_condition = ifstat.getHeadexprent().getCondition(); + + if(ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)if_condition).getFunctype() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory) + + FunctionExprent func = (FunctionExprent)if_condition; + Exprent first_param = func.getLstOperands().get(0); + Exprent second_param = func.getLstOperands().get(1); + + Statement ifbranch = ifstat.getIfstat(); + + if(second_param.type == Exprent.EXPRENT_CONST && ((ConstExprent)second_param).getExprType().type == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order + if(first_param.equals(exprent_value)) { // TODO: check for absence of side effects like method invocations etc. + if(ifbranch.type == Statement.TYPE_BASICBLOCK && ifbranch.getExprents().size() == 1 && // TODO: special check for IllegalStateException + ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) { + + ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge + + if(!ifstat.getFirst().getExprents().isEmpty()) { + stat.getExprents().addAll(0, ifstat.getFirst().getExprents()); + } + + for(StatEdge edge : ifstat.getAllPredecessorEdges()) { + + ifstat.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat); + stat.addPredecessor(edge); + } + + sequence.getStats().removeWithKey(ifstat.id); + sequence.setFirst(sequence.getStats().get(0)); + + return true; + } + } + } + } + } + } + } + } + + + for(Statement st: stat.getStats()) { + if(removeReturnCheck(st, mt)) { + return true; + } + } + + return false; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java new file mode 100644 index 0000000..6894f71 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java @@ -0,0 +1,731 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class IfHelper { + + + public static boolean mergeAllIfs(RootStatement root) { + + boolean res = mergeAllIfsRec(root, new HashSet<Integer>()); + + if(res) { + SequenceHelper.condenseSequences(root); + } + + return res; + } + + + private static boolean mergeAllIfsRec(Statement stat, HashSet<Integer> setReorderedIfs) { + + boolean res = false; + + if(stat.getExprents() == null) { + + for(;;) { + + boolean changed = false; + + for(Statement st: stat.getStats()) { + + res |= mergeAllIfsRec(st, setReorderedIfs); + + // collapse composed if's + if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) { + break; + } + } + + res |= changed; + + if(!changed) { + break; + } + } + } + + return res; + } + + + public static boolean mergeIfs(Statement statement, HashSet<Integer> setReorderedIfs) { + + if(statement.type != Statement.TYPE_IF && statement.type != Statement.TYPE_SEQUENCE) { + return false; + } + + boolean res = false; + + for(;;) { + + boolean updated = false; + + List<Statement> lst = new ArrayList<Statement>(); + if(statement.type == Statement.TYPE_IF) { + lst.add(statement); + } else { + lst.addAll(statement.getStats()); + } + + boolean stsingle = (lst.size() == 1); + + for(Statement stat: lst) { + + if(stat.type == Statement.TYPE_IF) { + IfNode rtnode = buildGraph((IfStatement)stat, stsingle); + + if(rtnode == null) { + continue; + } + + if(updated = collapseIfIf(rtnode)) { + break; + } + + if(!setReorderedIfs.contains(stat.id)) { + + if(updated = collapseIfElse(rtnode)) { + break; + } + + if(updated = collapseElse(rtnode)) { + break; + } + } + + if(updated = reorderIf((IfStatement)stat)) { + setReorderedIfs.add(stat.id); + break; + } + + } + } + + if(!updated) { + break; + } + + res |= updated; + } + + return res; + } + + private static boolean collapseIfIf(IfNode rtnode) { + + if(rtnode.edgetypes.get(0) == 0) { + IfNode ifbranch = rtnode.succs.get(0); + if(ifbranch.succs.size() == 2) { + + // if-if branch + if(ifbranch.succs.get(1).value == rtnode.succs.get(1).value) { + + IfStatement ifparent = (IfStatement)rtnode.value; + IfStatement ifchild = (IfStatement)ifbranch.value; + Statement ifinner = ifbranch.succs.get(0).value; + + if(ifchild.getFirst().getExprents().isEmpty()) { + + ifparent.getFirst().removeSuccessor(ifparent.getIfEdge()); + ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0)); + ifparent.getStats().removeWithKey(ifchild.id); + + if(ifbranch.edgetypes.get(0).intValue() == 1) { // target null + + ifparent.setIfstat(null); + + StatEdge ifedge = ifchild.getIfEdge(); + + ifchild.getFirst().removeSuccessor(ifedge); + ifedge.setSource(ifparent.getFirst()); + + if(ifedge.closure == ifchild) { + ifedge.closure = null; + } + ifparent.getFirst().addSuccessor(ifedge); + + ifparent.setIfEdge(ifedge); + } else { + ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); + + StatEdge ifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifparent.getFirst(), ifinner); + ifparent.getFirst().addSuccessor(ifedge); + ifparent.setIfEdge(ifedge); + ifparent.setIfstat(ifinner); + + ifparent.getStats().addWithKey(ifinner, ifinner.id); + ifinner.setParent(ifparent); + + if(!ifinner.getAllSuccessorEdges().isEmpty()) { + StatEdge edge = ifinner.getAllSuccessorEdges().get(0); + if(edge.closure == ifchild) { + edge.closure = null; + } + } + } + + // merge if conditions + IfExprent statexpr = ifparent.getHeadexprent(); + + List<Exprent> lstOperands = new ArrayList<Exprent>(); + lstOperands.add(statexpr.getCondition()); + lstOperands.add(ifchild.getHeadexprent().getCondition()); + + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); + + return true; + } + } + } + } + + return false; + } + + private static boolean collapseIfElse(IfNode rtnode) { + + if(rtnode.edgetypes.get(0).intValue() == 0) { + IfNode ifbranch = rtnode.succs.get(0); + if(ifbranch.succs.size() == 2) { + + // if-else branch + if(ifbranch.succs.get(0).value == rtnode.succs.get(1).value) { + + IfStatement ifparent = (IfStatement)rtnode.value; + IfStatement ifchild = (IfStatement)ifbranch.value; + + if(ifchild.getFirst().getExprents().isEmpty()) { + + ifparent.getFirst().removeSuccessor(ifparent.getIfEdge()); + ifchild.getFirst().removeSuccessor(ifchild.getIfEdge()); + ifparent.getStats().removeWithKey(ifchild.id); + + if(ifbranch.edgetypes.get(1).intValue() == 1 && + ifbranch.edgetypes.get(0).intValue() == 1) { // target null + + ifparent.setIfstat(null); + + StatEdge ifedge = ifchild.getAllSuccessorEdges().get(0); + + ifchild.removeSuccessor(ifedge); + ifedge.setSource(ifparent.getFirst()); + ifparent.getFirst().addSuccessor(ifedge); + + ifparent.setIfEdge(ifedge); + } else { + throw new RuntimeException("inconsistent if structure!"); + } + + // merge if conditions + IfExprent statexpr = ifparent.getHeadexprent(); + + List<Exprent> lstOperands = new ArrayList<Exprent>(); + lstOperands.add(statexpr.getCondition()); + lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{ifchild.getHeadexprent().getCondition()}))); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands)); + + return true; + } + } + } + } + + return false; + } + + + private static boolean collapseElse(IfNode rtnode) { + + if(rtnode.edgetypes.get(1).intValue() == 0) { + IfNode elsebranch = rtnode.succs.get(1); + if(elsebranch.succs.size() == 2) { + + // else-if or else-else branch + int path = elsebranch.succs.get(1).value == rtnode.succs.get(0).value?2: + (elsebranch.succs.get(0).value == rtnode.succs.get(0).value?1:0); + + if(path > 0) { + + IfStatement firstif = (IfStatement)rtnode.value; + IfStatement secondif = (IfStatement)elsebranch.value; + Statement parent = firstif.getParent(); + + if(secondif.getFirst().getExprents().isEmpty()) { + + firstif.getFirst().removeSuccessor(firstif.getIfEdge()); + + // remove first if + firstif.removeAllSuccessors(secondif); + + for(StatEdge edge: firstif.getAllPredecessorEdges()) { + if(!firstif.containsStatementStrict(edge.getSource())) { + firstif.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, secondif); + secondif.addPredecessor(edge); + } + } + + parent.getStats().removeWithKey(firstif.id); + if(parent.getFirst() == firstif) { + parent.setFirst(secondif); + } + + // merge if conditions + IfExprent statexpr = secondif.getHeadexprent(); + + List<Exprent> lstOperands = new ArrayList<Exprent>(); + lstOperands.add(firstif.getHeadexprent().getCondition()); + + if(path == 2) { + lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{lstOperands.get(0)}))); + } + + lstOperands.add(statexpr.getCondition()); + + statexpr.setCondition(new FunctionExprent(path==1?FunctionExprent.FUNCTION_COR:FunctionExprent.FUNCTION_CADD, lstOperands)); + + if(secondif.getFirst().getExprents().isEmpty() && + !firstif.getFirst().getExprents().isEmpty()) { + + secondif.replaceStatement(secondif.getFirst(), firstif.getFirst()); + } + + return true; + } + } + } else if(elsebranch.succs.size() == 1) { + + if(elsebranch.succs.get(0).value == rtnode.succs.get(0).value) { + + IfStatement firstif = (IfStatement)rtnode.value; + Statement second = elsebranch.value; + + firstif.removeAllSuccessors(second); + + for(StatEdge edge : second.getAllSuccessorEdges()) { + second.removeSuccessor(edge); + edge.setSource(firstif); + firstif.addSuccessor(edge); + } + + StatEdge ifedge = firstif.getIfEdge(); + firstif.getFirst().removeSuccessor(ifedge); + + second.addSuccessor(new StatEdge(ifedge.getType(), second, ifedge.getDestination(), ifedge.closure)); + + StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, firstif.getFirst(), second); + firstif.getFirst().addSuccessor(newifedge); + firstif.setIfstat(second); + + firstif.getStats().addWithKey(second, second.id); + second.setParent(firstif); + + firstif.getParent().getStats().removeWithKey(second.id); + + // negate the if condition + IfExprent statexpr = firstif.getHeadexprent(); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); + + return true; + } + + } + } + + return false; + } + + + private static IfNode buildGraph(IfStatement stat, boolean stsingle) { + + if(stat.iftype == IfStatement.IFTYPE_IFELSE) { + return null; + } + + IfNode res = new IfNode(stat); + + // if branch + Statement ifchild = stat.getIfstat(); + if(ifchild == null) { + StatEdge edge = stat.getIfEdge(); + res.addChild(new IfNode(edge.getDestination()), 1); + } else { + IfNode ifnode = new IfNode(ifchild); + res.addChild(ifnode, 0); + if(ifchild.type == Statement.TYPE_IF && ((IfStatement)ifchild).iftype == IfStatement.IFTYPE_IF) { + IfStatement stat2 = (IfStatement)ifchild; + Statement ifchild2 = stat2.getIfstat(); + if(ifchild2 == null) { + StatEdge edge = stat2.getIfEdge(); + ifnode.addChild(new IfNode(edge.getDestination()), 1); + } else { + ifnode.addChild(new IfNode(ifchild2), 0); + } + } + + if(!ifchild.getAllSuccessorEdges().isEmpty()) { + ifnode.addChild(new IfNode(ifchild.getAllSuccessorEdges().get(0).getDestination()), 1); + } + } + + // else branch + StatEdge edge = stat.getAllSuccessorEdges().get(0); + Statement elsechild = edge.getDestination(); + IfNode elsenode = new IfNode(elsechild); + + if(stsingle || edge.getType() != StatEdge.TYPE_REGULAR) { + res.addChild(elsenode, 1); + } else { + res.addChild(elsenode, 0); + if(elsechild.type == Statement.TYPE_IF && ((IfStatement)elsechild).iftype == IfStatement.IFTYPE_IF) { + IfStatement stat2 = (IfStatement)elsechild; + Statement ifchild2 = stat2.getIfstat(); + if(ifchild2 == null) { + elsenode.addChild(new IfNode(stat2.getIfEdge().getDestination()), 1); + } else { + elsenode.addChild(new IfNode(ifchild2), 0); + } + } + + if(!elsechild.getAllSuccessorEdges().isEmpty()) { + elsenode.addChild(new IfNode(elsechild.getAllSuccessorEdges().get(0).getDestination()), 1); + } + } + + return res; + } + + + // FIXME: rewrite the entire method!!! keep in mind finally exits!! + private static boolean reorderIf(IfStatement ifstat) { + + if(ifstat.iftype == IfStatement.IFTYPE_IFELSE) { + return false; + } + + boolean ifdirect = false, elsedirect = false;; + boolean noifstat = false, noelsestat = false; + boolean ifdirectpath = false, elsedirectpath = false; + + Statement parent = ifstat.getParent(); + Statement from = parent.type == Statement.TYPE_SEQUENCE?parent:ifstat; + + Statement next = getNextStatement(from); + + if(ifstat.getIfstat() == null) { + noifstat = true; + + if(ifstat.getIfEdge().getType() == StatEdge.TYPE_FINALLYEXIT) { + ifdirect = true; + } else { + ifdirect = MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination()); + } + } else { + List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges(); + if(!lstSuccs.isEmpty() && lstSuccs.get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { + ifdirect = true; + } else { + ifdirect = hasDirectEndEdge(ifstat.getIfstat(), from); + } + } + + Statement last = parent.type == Statement.TYPE_SEQUENCE?((SequenceStatement)parent).getStats().getLast():ifstat; + noelsestat = (last == ifstat); + + if(!last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == StatEdge.TYPE_FINALLYEXIT) { + elsedirect = true; + } else { + elsedirect = hasDirectEndEdge(last, from); + } + + if(!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) { + return false; + } + + if(!ifdirect && !noifstat) { + ifdirectpath = existsPath(ifstat, next); + } + + if(!elsedirect && !noelsestat) { + SequenceStatement sequence = (SequenceStatement)parent; + + for(int i=sequence.getStats().size()-1;i>=0;i--) { + Statement sttemp = sequence.getStats().get(i); + if(sttemp == ifstat) { + break; + } else { + if(elsedirectpath = existsPath(sttemp, next)) { + break; + } + } + } + } + + if((ifdirect || ifdirectpath) && (elsedirect || elsedirectpath) && !noifstat && !noelsestat) { // if - then - else + + SequenceStatement sequence = (SequenceStatement)parent; + + // build and cut the new else statement + List<Statement> lst = new ArrayList<Statement>(); + for(int i=sequence.getStats().size()-1;i>=0;i--) { + Statement sttemp = sequence.getStats().get(i); + if(sttemp == ifstat) { + break; + } else { + lst.add(0, sttemp); + } + } + + Statement stelse; + if(lst.size() == 1) { + stelse = lst.get(0); + } else { + stelse = new SequenceStatement(lst); + stelse.setAllParent(); + } + + ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); + for(Statement st: lst) { + sequence.getStats().removeWithKey(st.id); + } + + StatEdge elseedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse); + ifstat.getFirst().addSuccessor(elseedge); + ifstat.setElsestat(stelse); + ifstat.setElseEdge(elseedge); + + ifstat.getStats().addWithKey(stelse, stelse.id); + stelse.setParent(ifstat); + +// if(next.type != Statement.TYPE_DUMMYEXIT && (ifdirect || elsedirect)) { +// StatEdge breakedge = new StatEdge(StatEdge.TYPE_BREAK, ifstat, next); +// sequence.addLabeledEdge(breakedge); +// ifstat.addSuccessor(breakedge); +// } + + ifstat.iftype = IfStatement.IFTYPE_IFELSE; + + } else if(ifdirect && (!elsedirect || (noifstat && !noelsestat))) { // if - then + + // negate the if condition + IfExprent statexpr = ifstat.getHeadexprent(); + statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{statexpr.getCondition()}))); + + if(noelsestat) { + StatEdge ifedge = ifstat.getIfEdge(); + StatEdge elseedge = ifstat.getAllSuccessorEdges().get(0); + + if(noifstat) { + ifstat.getFirst().removeSuccessor(ifedge); + ifstat.removeSuccessor(elseedge); + + ifedge.setSource(ifstat); + elseedge.setSource(ifstat.getFirst()); + + ifstat.addSuccessor(ifedge); + ifstat.getFirst().addSuccessor(elseedge); + + ifstat.setIfEdge(elseedge); + } else { + Statement ifbranch = ifstat.getIfstat(); + SequenceStatement newseq = new SequenceStatement(Arrays.asList(new Statement[] {ifstat, ifbranch})); + + ifstat.getFirst().removeSuccessor(ifedge); + ifstat.getStats().removeWithKey(ifbranch.id); + ifstat.setIfstat(null); + + ifstat.removeSuccessor(elseedge); + elseedge.setSource(ifstat.getFirst()); + ifstat.getFirst().addSuccessor(elseedge); + + ifstat.setIfEdge(elseedge); + + ifstat.getParent().replaceStatement(ifstat, newseq); + newseq.setAllParent(); + + ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch)); + } + } else { + + SequenceStatement sequence = (SequenceStatement)parent; + + // build and cut the new else statement + List<Statement> lst = new ArrayList<Statement>(); + for(int i=sequence.getStats().size()-1;i>=0;i--) { + Statement sttemp = sequence.getStats().get(i); + if(sttemp == ifstat) { + break; + } else { + lst.add(0, sttemp); + } + } + + Statement stelse; + if(lst.size() == 1) { + stelse = lst.get(0); + } else { + stelse = new SequenceStatement(lst); + stelse.setAllParent(); + } + + ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); + for(Statement st: lst) { + sequence.getStats().removeWithKey(st.id); + } + + if(noifstat) { + StatEdge ifedge = ifstat.getIfEdge(); + + ifstat.getFirst().removeSuccessor(ifedge); + ifedge.setSource(ifstat); + ifstat.addSuccessor(ifedge); + } else { + Statement ifbranch = ifstat.getIfstat(); + + ifstat.getFirst().removeSuccessor(ifstat.getIfEdge()); + ifstat.getStats().removeWithKey(ifbranch.id); + + ifstat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, ifstat, ifbranch)); + + sequence.getStats().addWithKey(ifbranch, ifbranch.id); + ifbranch.setParent(sequence); + } + + StatEdge newifedge = new StatEdge(StatEdge.TYPE_REGULAR, ifstat.getFirst(), stelse); + ifstat.getFirst().addSuccessor(newifedge); + ifstat.setIfstat(stelse); + ifstat.setIfEdge(newifedge); + + ifstat.getStats().addWithKey(stelse, stelse.id); + stelse.setParent(ifstat); + + } + } else { + return false; + } + + return true; + } + + private static boolean hasDirectEndEdge(Statement stat, Statement from) { + + for(StatEdge edge: stat.getAllSuccessorEdges()) { + if(MergeHelper.isDirectPath(from, edge.getDestination())) { + return true; + } + } + + if(stat.getExprents() == null) { + switch(stat.type) { + case Statement.TYPE_SEQUENCE: + return hasDirectEndEdge(stat.getStats().getLast(), from); + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + for(Statement st: stat.getStats()) { + if(hasDirectEndEdge(st, from)) { + return true; + } + } + break; + case Statement.TYPE_IF: + IfStatement ifstat = (IfStatement)stat; + if(ifstat.iftype == IfStatement.IFTYPE_IFELSE) { + return hasDirectEndEdge(ifstat.getIfstat(), from) || + hasDirectEndEdge(ifstat.getElsestat(), from); + } + break; + case Statement.TYPE_SYNCRONIZED: + return hasDirectEndEdge(stat.getStats().get(1), from); + case Statement.TYPE_SWITCH: + for(Statement st: stat.getStats()) { + if(hasDirectEndEdge(st, from)) { + return true; + } + } + } + } + + return false; + } + + + private static Statement getNextStatement(Statement stat) { + + Statement parent = stat.getParent(); + switch(parent.type) { + case Statement.TYPE_ROOT: + return ((RootStatement)parent).getDummyExit(); + case Statement.TYPE_DO: + return parent; + case Statement.TYPE_SEQUENCE: + SequenceStatement sequence = (SequenceStatement)parent; + if(sequence.getStats().getLast() != stat) { + for(int i=sequence.getStats().size()-1;i>=0;i--) { + if(sequence.getStats().get(i) == stat) { + return sequence.getStats().get(i+1); + } + } + } + } + + return getNextStatement(parent); + } + + private static boolean existsPath(Statement from, Statement to) { + + for(StatEdge edge: to.getAllPredecessorEdges()) { + if(from.containsStatementStrict(edge.getSource())) { + return true; + } + } + + return false; + } + + private static class IfNode { + public Statement value; + + public List<IfNode> succs = new ArrayList<IfNode>(); + public List<Integer> edgetypes = new ArrayList<Integer>(); + + public IfNode(Statement value) { + this.value = value; + } + + public void addChild(IfNode child, int type) { + succs.add(child); + edgetypes.add(new Integer(type)); + } + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java new file mode 100644 index 0000000..3fe181a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java @@ -0,0 +1,223 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; + + +public class InlineSingleBlockHelper { + + + + public static boolean inlineSingleBlocks(RootStatement root) { + + boolean res = inlineSingleBlocksRec(root); + + if(res) { + SequenceHelper.condenseSequences(root); + } + + return res; + } + + private static boolean inlineSingleBlocksRec(Statement stat) { + + boolean res = false; + + for(Statement st : stat.getStats()) { + res |= inlineSingleBlocksRec(st); + } + + if(stat.type == Statement.TYPE_SEQUENCE) { + + SequenceStatement seq = (SequenceStatement)stat; + for(int i=1;i<seq.getStats().size();i++) { + if(isInlineable(seq, i)) { + inlineBlock(seq, i); + return true; + } + } + } + + return res; + } + + private static void inlineBlock(SequenceStatement seq, int index) { + + Statement first = seq.getStats().get(index); + Statement pre = seq.getStats().get(index-1); + pre.removeSuccessor(pre.getAllSuccessorEdges().get(0)); // single regular edge + + StatEdge edge = first.getPredecessorEdges(StatEdge.TYPE_BREAK).get(0); + Statement source = edge.getSource(); + Statement parent = source.getParent(); + source.removeSuccessor(edge); + + List<Statement> lst = new ArrayList<Statement>(); + for(int i=seq.getStats().size()-1;i>=index;i--) { + lst.add(0, seq.getStats().remove(i)); + } + + if(parent.type == Statement.TYPE_IF && ((IfStatement)parent).iftype == IfStatement.IFTYPE_IF && + source == parent.getFirst()) { + IfStatement ifparent = (IfStatement)parent; + SequenceStatement block = new SequenceStatement(lst); + block.setAllParent(); + + StatEdge newedge = new StatEdge(StatEdge.TYPE_REGULAR, source, block); + source.addSuccessor(newedge); + ifparent.setIfEdge(newedge); + ifparent.setIfstat(block); + + ifparent.getStats().addWithKey(block, block.id); + block.setParent(ifparent); + + } else { + lst.add(0, source); + + SequenceStatement block = new SequenceStatement(lst); + block.setAllParent(); + + parent.replaceStatement(source, block); + + // LabelHelper.lowContinueLabels not applicable because of forward continue edges + // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>()); + // do it by hand + for(StatEdge prededge : block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { + + block.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, source); + source.addPredecessor(prededge); + + source.addLabeledEdge(prededge); + } + + + if(parent.type == Statement.TYPE_SWITCH) { + ((SwitchStatement)parent).sortEdgesAndNodes(); + } + + source.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, source, first)); + } + + } + + private static boolean isInlineable(SequenceStatement seq, int index) { + + Statement first = seq.getStats().get(index); + Statement pre = seq.getStats().get(index-1); + + if(pre.hasBasicSuccEdge()) { + return false; + } + + + List<StatEdge> lst = first.getPredecessorEdges(StatEdge.TYPE_BREAK); + + if(lst.size() == 1) { + StatEdge edge = lst.get(0); + + if(sameCatchRanges(edge)) { + if(edge.explicit) { + return true; + } else { + for(int i=index;i<seq.getStats().size();i++) { + if(!noExitLabels(seq.getStats().get(i), seq)) { + return false; + } + } + return true; + } + } + // FIXME: count labels properly + } + + return false; + } + + private static boolean sameCatchRanges(StatEdge edge) { + + Statement from = edge.getSource(); + Statement to = edge.getDestination(); + + for(;;) { + + Statement parent = from.getParent(); + if(parent.containsStatementStrict(to)) { + break; + } + + if(parent.type == Statement.TYPE_TRYCATCH || + parent.type == Statement.TYPE_CATCHALL) { + if(parent.getFirst() == from) { + return false; + } + } else if(parent.type == Statement.TYPE_SYNCRONIZED) { + if(parent.getStats().get(1) == from) { + return false; + } + } + + from = parent; + } + + return true; + } + + private static boolean noExitLabels(Statement block, Statement sequence) { + + for(StatEdge edge : block.getAllSuccessorEdges()) { + if(edge.getType() != StatEdge.TYPE_REGULAR && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { + if(!sequence.containsStatementStrict(edge.getDestination())) { + return false; + } + } + } + + for(Statement st : block.getStats()) { + if(!noExitLabels(st, sequence)) { + return false; + } + } + + return true; + } + + public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { + + if(closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { + + Statement parent = source.getParent(); + + if(parent == closure) { + return false; + } else { + return parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH || + isBreakEdgeLabeled(parent, closure); + } + } else { + return true; + } + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java new file mode 100644 index 0000000..6d7a897 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java @@ -0,0 +1,521 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; + + +public class LabelHelper { + + + public static void cleanUpEdges(RootStatement root) { + + resetAllEdges(root); + + removeNonImmediateEdges(root); + + liftClosures(root); + + lowContinueLabels(root, new HashSet<StatEdge>()); + + lowClosures(root); + + } + + public static void identifyLabels(RootStatement root) { + + setExplicitEdges(root); + + hideDefaultSwitchEdges(root); + + processStatementLabel(root); + + setRetEdgesUnlabeled(root); + } + + private static void liftClosures(Statement stat) { + + for(StatEdge edge : stat.getAllSuccessorEdges()) { + switch(edge.getType()) { + case StatEdge.TYPE_CONTINUE: + if(edge.getDestination() != edge.closure) { + edge.getDestination().addLabeledEdge(edge); + } + break; + case StatEdge.TYPE_BREAK: + Statement dest = edge.getDestination(); + if(dest.type != Statement.TYPE_DUMMYEXIT) { + Statement parent = dest.getParent(); + + List<Statement> lst = new ArrayList<Statement>(); + if(parent.type == Statement.TYPE_SEQUENCE) { + lst.addAll(((SequenceStatement)parent).getStats()); + } else if(parent.type == Statement.TYPE_SWITCH) { + lst.addAll(((SwitchStatement)parent).getCaseStatements()); + } + + for(int i=0;i<lst.size();i++) { + if(lst.get(i) == dest) { + lst.get(i-1).addLabeledEdge(edge); + break; + } + } + } + } + } + + for(Statement st : stat.getStats()) { + liftClosures(st); + } + + } + + private static void removeNonImmediateEdges(Statement stat) { + + for(Statement st : stat.getStats()) { + removeNonImmediateEdges(st); + } + + if(!stat.hasBasicSuccEdge()) { + for(StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_CONTINUE | StatEdge.TYPE_BREAK)) { + stat.removeSuccessor(edge); + } + } + } + + public static void lowContinueLabels(Statement stat, HashSet<StatEdge> edges) { + + boolean ok = (stat.type != Statement.TYPE_DO); + if(!ok) { + DoStatement dostat = (DoStatement)stat; + ok = dostat.getLooptype() == DoStatement.LOOP_DO || + dostat.getLooptype() == DoStatement.LOOP_WHILE || + (dostat.getLooptype() == DoStatement.LOOP_FOR && dostat.getIncExprent() == null); + } + + if(ok) { + edges.addAll(stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE)); + } + + if(ok && stat.type == Statement.TYPE_DO) { + for(StatEdge edge: edges) { + if(stat.containsStatementStrict(edge.getSource())) { + + edge.getDestination().removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, stat); + stat.addPredecessor(edge); + + stat.addLabeledEdge(edge); + } + } + } + + for(Statement st: stat.getStats()) { + if(st == stat.getFirst()) { + lowContinueLabels(st, edges); + } else { + lowContinueLabels(st, new HashSet<StatEdge>()); + } + } + } + + public static void lowClosures(Statement stat) { + + for(StatEdge edge : new ArrayList<StatEdge>(stat.getLabelEdges())) { + + if(edge.getType() == StatEdge.TYPE_BREAK) { // FIXME: ? + for(Statement st : stat.getStats()) { + if(st.containsStatementStrict(edge.getSource())) { + if(MergeHelper.isDirectPath(st, edge.getDestination())) { + st.addLabeledEdge(edge); + } + } + } + } + } + + for(Statement st: stat.getStats()) { + lowClosures(st); + } + + } + + private static void resetAllEdges(Statement stat) { + + for(Statement st: stat.getStats()) { + resetAllEdges(st); + } + + for(StatEdge edge: stat.getAllSuccessorEdges()) { + edge.explicit = true; + edge.labeled = true; + } + } + + private static void setRetEdgesUnlabeled(RootStatement root) { + Statement exit = root.getDummyExit(); + for(StatEdge edge: exit.getAllPredecessorEdges()) { + List<Exprent> lst = edge.getSource().getExprents(); + if(edge.getType() == StatEdge.TYPE_FINALLYEXIT || (lst != null && !lst.isEmpty() && + lst.get(lst.size()-1).type == Exprent.EXPRENT_EXIT)) { + edge.labeled = false; + } + } + } + + private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) { + + HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<Statement, List<StatEdge>>(); + + if(stat.getExprents() != null) { + return mapEdges; + } + + + switch(stat.type) { + case Statement.TYPE_TRYCATCH: + case Statement.TYPE_CATCHALL: + + for(Statement st : stat.getStats()) { + HashMap<Statement, List<StatEdge>> mapEdges1 = setExplicitEdges(st); + processEdgesWithNext(st, mapEdges1, null); + + if(stat.type == Statement.TYPE_TRYCATCH || st == stat.getFirst()) { // edges leaving a finally catch block are always explicit + // merge the maps + if(mapEdges1 != null) { + for(Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) { + if(mapEdges.containsKey(entr.getKey())) { + mapEdges.get(entr.getKey()).addAll(entr.getValue()); + } else { + mapEdges.put(entr.getKey(), entr.getValue()); + } + } + } + } + } + + break; + case Statement.TYPE_DO: + mapEdges = setExplicitEdges(stat.getFirst()); + processEdgesWithNext(stat.getFirst(), mapEdges, stat); + break; + case Statement.TYPE_IF: + IfStatement ifstat = (IfStatement)stat; + // head statement is a basic block + if(ifstat.getIfstat() == null) { // empty if + processEdgesWithNext(ifstat.getFirst(), mapEdges, null); + } else { + if(ifstat.getIfstat() != null) { + mapEdges = setExplicitEdges(ifstat.getIfstat()); + processEdgesWithNext(ifstat.getIfstat(), mapEdges, null); + } + + HashMap<Statement, List<StatEdge>> mapEdges1 = null; + if(ifstat.getElsestat() != null) { + mapEdges1 = setExplicitEdges(ifstat.getElsestat()); + processEdgesWithNext(ifstat.getElsestat(), mapEdges1, null); + } + + // merge the maps + if(mapEdges1 != null) { + for(Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) { + if(mapEdges.containsKey(entr.getKey())) { + mapEdges.get(entr.getKey()).addAll(entr.getValue()); + } else { + mapEdges.put(entr.getKey(), entr.getValue()); + } + } + } + } + break; + case Statement.TYPE_ROOT: + mapEdges = setExplicitEdges(stat.getFirst()); + processEdgesWithNext(stat.getFirst(), mapEdges, ((RootStatement)stat).getDummyExit()); + break; + case Statement.TYPE_SEQUENCE: + int index = 0; + while(index < stat.getStats().size()-1) { + Statement st = stat.getStats().get(index); + processEdgesWithNext(st, setExplicitEdges(st), stat.getStats().get(index+1)); + index++; + } + + Statement st = stat.getStats().get(index); + mapEdges = setExplicitEdges(st); + processEdgesWithNext(st, mapEdges, null); + break; + case Statement.TYPE_SWITCH: + SwitchStatement swst = (SwitchStatement)stat; + + for(int i=0;i<swst.getCaseStatements().size()-1;i++) { + Statement stt = swst.getCaseStatements().get(i); + Statement stnext = swst.getCaseStatements().get(i+1); + + if(stnext.getExprents() != null && stnext.getExprents().isEmpty()) { + stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); + } + processEdgesWithNext(stt, setExplicitEdges(stt), stnext); + } + + int last = swst.getCaseStatements().size()-1; + if(last >= 0) { // empty switch possible + Statement stlast = swst.getCaseStatements().get(last); + if(stlast.getExprents() != null && stlast.getExprents().isEmpty()) { + StatEdge edge = stlast.getAllSuccessorEdges().get(0); + mapEdges.put(edge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {edge}))); + } else { + mapEdges = setExplicitEdges(stlast); + processEdgesWithNext(stlast, mapEdges, null); + } + } + + break; + case Statement.TYPE_SYNCRONIZED: + SynchronizedStatement synstat = (SynchronizedStatement)stat; + + processEdgesWithNext(synstat.getFirst(), setExplicitEdges(stat.getFirst()), synstat.getBody()); // FIXME: basic block? + mapEdges = setExplicitEdges(synstat.getBody()); + processEdgesWithNext(synstat.getBody(), mapEdges, null); + } + + + return mapEdges; + } + + private static void processEdgesWithNext(Statement stat, HashMap<Statement, List<StatEdge>> mapEdges, Statement next) { + + StatEdge statedge = null; + + List<StatEdge> lstSuccs = stat.getAllSuccessorEdges(); + if(!lstSuccs.isEmpty()) { + statedge = lstSuccs.get(0); + + if(statedge.getDestination() == next) { + statedge.explicit = false; + statedge = null; + } else { + next = statedge.getDestination(); + } + } + + // no next for a do statement + if(stat.type == Statement.TYPE_DO && ((DoStatement)stat).getLooptype() == DoStatement.LOOP_DO) { + next = null; + } + + if(next == null) { + if(mapEdges.size() == 1) { + List<StatEdge> lstEdges = mapEdges.values().iterator().next(); + if(lstEdges.size() > 1 && mapEdges.keySet().iterator().next().type != Statement.TYPE_DUMMYEXIT) { + StatEdge edge_example = lstEdges.get(0); + + Statement closure = stat.getParent(); + if(!closure.containsStatementStrict(edge_example.closure)) { + closure = edge_example.closure; + } + + StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure); + stat.addSuccessor(newedge); + + for(StatEdge edge : lstEdges) { + edge.explicit = false; + } + + mapEdges.put(newedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {newedge}))); + } + } + } else { + + boolean implfound = false; + + for(Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) { + if(entr.getKey() == next) { + for(StatEdge edge : entr.getValue()) { + edge.explicit = false; + } + implfound = true; + break; + } + } + + if(stat.getAllSuccessorEdges().isEmpty() && !implfound) { + List<StatEdge> lstEdges = null; + for(Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) { + if(entr.getKey().type != Statement.TYPE_DUMMYEXIT && + (lstEdges == null || entr.getValue().size() > lstEdges.size())) { + lstEdges = entr.getValue(); + } + } + + if(lstEdges != null && lstEdges.size() > 1) { + StatEdge edge_example = lstEdges.get(0); + + Statement closure = stat.getParent(); + if(!closure.containsStatementStrict(edge_example.closure)) { + closure = edge_example.closure; + } + + StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure); + stat.addSuccessor(newedge); + + for(StatEdge edge : lstEdges) { + edge.explicit = false; + } + } + } + + mapEdges.clear(); + } + + if(statedge != null) { + mapEdges.put(statedge.getDestination(), new ArrayList<StatEdge>(Arrays.asList(new StatEdge[] {statedge}))); + } + + } + + private static void hideDefaultSwitchEdges(Statement stat) { + + if(stat.type == Statement.TYPE_SWITCH) { + SwitchStatement swst = (SwitchStatement)stat; + + int last = swst.getCaseStatements().size()-1; + if(last >= 0) { // empty switch possible + Statement stlast = swst.getCaseStatements().get(last); + + if(stlast.getExprents() != null && stlast.getExprents().isEmpty()) { + if(!stlast.getAllSuccessorEdges().get(0).explicit) { + List<StatEdge> lstEdges = swst.getCaseEdges().get(last); + lstEdges.remove(swst.getDefault_edge()); + + if(lstEdges.isEmpty()) { + swst.getCaseStatements().remove(last); + swst.getCaseEdges().remove(last); + } + } + } + } + } + + for(Statement st : stat.getStats()) { + hideDefaultSwitchEdges(st); + } + + } + + private static HashSet<Statement>[] processStatementLabel(Statement stat) { + + HashSet<Statement> setBreak = new HashSet<Statement>(); + HashSet<Statement> setContinue = new HashSet<Statement>(); + + if(stat.getExprents() == null) { + for(Statement st: stat.getStats()) { + HashSet<Statement>[] arr = processStatementLabel(st); + + setBreak.addAll(arr[0]); + setContinue.addAll(arr[1]); + } + + boolean shieldtype = (stat.type == Statement.TYPE_DO || stat.type == Statement.TYPE_SWITCH); + + for(StatEdge edge: stat.getLabelEdges()) { + if(edge.explicit) { + if(shieldtype && ((edge.getType() == StatEdge.TYPE_BREAK && setBreak.contains(edge.getSource())) || + (edge.getType() == StatEdge.TYPE_CONTINUE && setContinue.contains(edge.getSource())))) { + edge.labeled = false; + } + } + } + + switch(stat.type) { + case Statement.TYPE_DO: + setContinue.clear(); + case Statement.TYPE_SWITCH: + setBreak.clear(); + } + } + + setBreak.add(stat); + setContinue.add(stat); + + return new HashSet[] {setBreak, setContinue}; + } + + public static void replaceContinueWithBreak(Statement stat) { + + if(stat.type == Statement.TYPE_DO) { + + List<StatEdge> lst = stat.getPredecessorEdges(StatEdge.TYPE_CONTINUE); + + for(StatEdge edge : lst) { + + if(edge.explicit) { + Statement minclosure = getMinContinueClosure(edge); + + if(minclosure != edge.closure && + !InlineSingleBlockHelper.isBreakEdgeLabeled(edge.getSource(), minclosure)) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK); + edge.labeled = false; + minclosure.addLabeledEdge(edge); + } + } + } + } + + for(Statement st : stat.getStats()) { + replaceContinueWithBreak(st); + } + + } + + private static Statement getMinContinueClosure(StatEdge edge) { + + Statement closure = edge.closure; + for(;;) { + + boolean found = false; + + for(Statement st : closure.getStats()) { + if(st.containsStatementStrict(edge.getSource())) { + if(MergeHelper.isDirectPath(st, edge.getDestination())) { + closure = st; + found = true; + break; + } + } + } + + if(!found) { + break; + } + } + + return closure; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java new file mode 100644 index 0000000..5e0130b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java @@ -0,0 +1,209 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + + +public class LoopExtractHelper { + + + + public static boolean extractLoops(Statement root) { + + boolean res = (extractLoopsRec(root) != 0); + + if(res) { + SequenceHelper.condenseSequences(root); + } + + return res; + } + + + private static int extractLoopsRec(Statement stat) { + + boolean res = false; + + for(;;) { + + boolean updated = false; + + for(Statement st: stat.getStats()) { + int extr = extractLoopsRec(st); + res |= (extr != 0); + + if(extr == 2) { + updated = true; + break; + } + } + + if(!updated) { + break; + } + } + + if(stat.type == Statement.TYPE_DO) { + if(extractLoop((DoStatement)stat)) { + return 2; + } + } + + return res?1:0; + } + + + + private static boolean extractLoop(DoStatement stat) { + + if(stat.getLooptype() != DoStatement.LOOP_DO) { + return false; + } + + for(StatEdge edge: stat.getLabelEdges()) { + if(edge.getType() != StatEdge.TYPE_CONTINUE && edge.getDestination().type != Statement.TYPE_DUMMYEXIT) { + return false; + } + } + + if(!extractLastIf(stat)) { + return extractFirstIf(stat); + } else { + return true; + } + } + + private static boolean extractLastIf(DoStatement stat) { + + // search for an if condition at the end of the loop + Statement last = stat.getFirst(); + while(last.type == Statement.TYPE_SEQUENCE) { + last = last.getStats().getLast(); + } + + if(last.type == Statement.TYPE_IF) { + IfStatement lastif = (IfStatement)last; + if(lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() != null) { + Statement ifstat = lastif.getIfstat(); + StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); + + if(elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat) { + + Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); + set.remove(last); + + if(set.isEmpty()) { // no direct continues in a do{}while loop + if(isExternStatement(stat, ifstat, ifstat)) { + extractIfBlock(stat, lastif); + return true; + } + } + } + } + } + return false; + } + + private static boolean extractFirstIf(DoStatement stat) { + + // search for an if condition at the entrance of the loop + Statement first = stat.getFirst(); + while(first.type == Statement.TYPE_SEQUENCE) { + first = first.getFirst(); + } + + // found an if statement + if(first.type == Statement.TYPE_IF) { + IfStatement firstif = (IfStatement)first; + + if(firstif.getFirst().getExprents().isEmpty()) { + + if(firstif.iftype == IfStatement.IFTYPE_IF && firstif.getIfstat()!=null) { + Statement ifstat = firstif.getIfstat(); + + if(isExternStatement(stat, ifstat, ifstat)) { + extractIfBlock(stat, firstif); + return true; + } + } + } + } + return false; + } + + + + private static boolean isExternStatement(DoStatement loop, Statement block, Statement stat) { + + for(StatEdge edge: stat.getAllSuccessorEdges()) { + if(loop.containsStatement(edge.getDestination()) && + !block.containsStatement(edge.getDestination())) { + return false; + } + } + + for(Statement st: stat.getStats()) { + if(!isExternStatement(loop, block, st)) { + return false; + } + } + + return true; + } + + + private static void extractIfBlock(DoStatement loop, IfStatement ifstat) { + + Statement target = ifstat.getIfstat(); + StatEdge ifedge = ifstat.getIfEdge(); + + ifstat.setIfstat(null); + ifedge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, ifedge, StatEdge.TYPE_BREAK); + ifedge.closure = loop; + ifstat.getStats().removeWithKey(target.id); + + loop.addLabeledEdge(ifedge); + + SequenceStatement block = new SequenceStatement(Arrays.asList(new Statement[] {loop, target})); + loop.getParent().replaceStatement(loop, block); + block.setAllParent(); + + loop.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, loop, target)); + + for(StatEdge edge: new ArrayList<StatEdge>(block.getLabelEdges())) { + if(edge.getType() == StatEdge.TYPE_CONTINUE || edge == ifedge) { + loop.addLabeledEdge(edge); + } + } + + for(StatEdge edge: block.getPredecessorEdges(StatEdge.TYPE_CONTINUE)) { + if(loop.containsStatementStrict(edge.getSource())) { + block.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, loop); + loop.addPredecessor(edge); + } + } + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java new file mode 100644 index 0000000..5811ef8 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java @@ -0,0 +1,207 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; + +public class LowBreakHelper { + + public static void lowBreakLabels(Statement root) { + + lowBreakLabelsRec(root); + + liftBreakLabels(root); + } + + private static void lowBreakLabelsRec(Statement stat) { + + for(;;) { + + boolean found = false; + + for(StatEdge edge: stat.getLabelEdges()) { + if(edge.getType() == StatEdge.TYPE_BREAK) { + Statement minclosure = getMinClosure(stat, edge.getSource()); + if(minclosure != stat) { + minclosure.addLabeledEdge(edge); + edge.labeled = isBreakEdgeLabeled(edge.getSource(), minclosure); + found = true; + break; + } + } + } + + if(!found) { + break; + } + } + + for(Statement st: stat.getStats()) { + lowBreakLabelsRec(st); + } + + } + + public static boolean isBreakEdgeLabeled(Statement source, Statement closure) { + + if(closure.type == Statement.TYPE_DO || closure.type == Statement.TYPE_SWITCH) { + + Statement parent = source.getParent(); + + if(parent == closure) { + return false; + } else { + return isBreakEdgeLabeled(parent, closure) || + (parent.type == Statement.TYPE_DO || parent.type == Statement.TYPE_SWITCH); + } + } else { + return true; + } + } + + public static Statement getMinClosure(Statement closure, Statement source) { + + for(;;) { + + Statement newclosure = null; + + switch(closure.type) { + case Statement.TYPE_SEQUENCE: + Statement last = closure.getStats().getLast(); + + if(isOkClosure(closure, source, last)) { + newclosure = last; + } + break; + case Statement.TYPE_IF: + IfStatement ifclosure = (IfStatement)closure; + if(isOkClosure(closure, source, ifclosure.getIfstat())) { + newclosure = ifclosure.getIfstat(); + } else if(isOkClosure(closure, source, ifclosure.getElsestat())) { + newclosure = ifclosure.getElsestat(); + } + break; + case Statement.TYPE_TRYCATCH: + for(Statement st: closure.getStats()) { + if(isOkClosure(closure, source, st)) { + newclosure = st; + break; + } + } + break; + case Statement.TYPE_SYNCRONIZED: + Statement body = ((SynchronizedStatement)closure).getBody(); + + if(isOkClosure(closure, source, body)) { + newclosure = body; + } + } + + if(newclosure == null) { + break; + } + + closure = newclosure; + } + + return closure; + } + + private static boolean isOkClosure(Statement closure, Statement source, Statement stat) { + + boolean ok = false; + + if(stat != null && stat.containsStatementStrict(source)) { + + List<StatEdge> lst = stat.getAllSuccessorEdges(); + + ok = lst.isEmpty(); + if(!ok) { + StatEdge edge = lst.get(0); + ok = (edge.closure == closure && edge.getType() == StatEdge.TYPE_BREAK); + } + } + + return ok; + } + + + private static void liftBreakLabels(Statement stat) { + + for(Statement st: stat.getStats()) { + liftBreakLabels(st); + } + + + for(;;) { + + boolean found = false; + + for(StatEdge edge: stat.getLabelEdges()) { + if(edge.explicit && edge.labeled && edge.getType() == StatEdge.TYPE_BREAK) { + + Statement newclosure = getMaxBreakLift(stat, edge); + + if(newclosure != null) { + newclosure.addLabeledEdge(edge); + edge.labeled = isBreakEdgeLabeled(edge.getSource(), newclosure); + + found = true; + break; + } + } + } + + if(!found) { + break; + } + } + + } + + private static Statement getMaxBreakLift(Statement stat, StatEdge edge) { + + Statement closure = null; + Statement newclosure = stat; + + while((newclosure = getNextBreakLift(newclosure, edge)) != null) { + closure = newclosure; + } + + return closure; + } + + private static Statement getNextBreakLift(Statement stat, StatEdge edge) { + + Statement closure = stat.getParent(); + + while(closure!=null && !closure.containsStatementStrict(edge.getDestination())) { + + boolean labeled = isBreakEdgeLabeled(edge.getSource(), closure); + if(closure.isLabeled() || !labeled) { + return closure; + } + + closure = closure.getParent(); + } + + return null; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java new file mode 100644 index 0000000..b9149e6 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java @@ -0,0 +1,416 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; + +public class MergeHelper { + + public static void enhanceLoops(Statement root) { + + while(enhanceLoopsRec(root)); + + SequenceHelper.condenseSequences(root); + } + + private static boolean enhanceLoopsRec(Statement stat) { + + boolean res = false; + + for(Statement st: stat.getStats()) { + if(st.getExprents() == null) { + res |= enhanceLoopsRec(st); + } + } + + if(stat.type == Statement.TYPE_DO) { + res |= enhanceLoop((DoStatement)stat); + } + + return res; + } + + private static boolean enhanceLoop(DoStatement stat) { + + int oldloop = stat.getLooptype(); + + switch(oldloop) { + case DoStatement.LOOP_DO: + + // identify a while loop + if(matchWhile(stat)) { + // identify a for loop - subtype of while + matchFor(stat); + } else { + // identify a do{}while loop + matchDoWhile(stat); + } + + break; + case DoStatement.LOOP_WHILE: + matchFor(stat); + } + + return (stat.getLooptype() != oldloop); + } + + private static boolean matchDoWhile(DoStatement stat) { + + // search for an if condition at the end of the loop + Statement last = stat.getFirst(); + while(last.type == Statement.TYPE_SEQUENCE) { + last = last.getStats().getLast(); + } + + if(last.type == Statement.TYPE_IF) { + IfStatement lastif = (IfStatement)last; + if(lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() == null) { + StatEdge ifedge = lastif.getIfEdge(); + StatEdge elseedge = lastif.getAllSuccessorEdges().get(0); + + if((ifedge.getType() == StatEdge.TYPE_BREAK && elseedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.closure == stat + && isDirectPath(stat, ifedge.getDestination())) || + (ifedge.getType() == StatEdge.TYPE_CONTINUE && elseedge.getType() == StatEdge.TYPE_BREAK && ifedge.closure == stat + && isDirectPath(stat, elseedge.getDestination()))) { + + Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); + set.remove(last); + + if(!set.isEmpty()) { + return false; + } + + + stat.setLooptype(DoStatement.LOOP_DOWHILE); + + IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy(); + if(ifedge.getType() == StatEdge.TYPE_BREAK) { + ifexpr.negateIf(); + } + stat.setConditionExprent(ifexpr.getCondition()); + lastif.getFirst().removeSuccessor(ifedge); + lastif.removeSuccessor(elseedge); + + // remove empty if + if(lastif.getFirst().getExprents().isEmpty()) { + removeLastEmptyStatement(stat, lastif); + } else { + lastif.setExprents(lastif.getFirst().getExprents()); + + StatEdge newedge = new StatEdge(StatEdge.TYPE_CONTINUE, lastif, stat); + lastif.addSuccessor(newedge); + stat.addLabeledEdge(newedge); + } + + if(stat.getAllSuccessorEdges().isEmpty()) { + StatEdge edge = elseedge.getType() == StatEdge.TYPE_CONTINUE?ifedge:elseedge; + + edge.setSource(stat); + if(edge.closure == stat) { + edge.closure = stat.getParent(); + } + stat.addSuccessor(edge); + } + + return true; + } + } + } + return false; + } + + private static boolean matchWhile(DoStatement stat) { + + // search for an if condition at the entrance of the loop + Statement first = stat.getFirst(); + while(first.type == Statement.TYPE_SEQUENCE) { + first = first.getFirst(); + } + + // found an if statement + if(first.type == Statement.TYPE_IF) { + IfStatement firstif = (IfStatement)first; + + if(firstif.getFirst().getExprents().isEmpty()) { + + if(firstif.iftype == IfStatement.IFTYPE_IF) { + if(firstif.getIfstat()==null) { + StatEdge ifedge = firstif.getIfEdge(); + if(isDirectPath(stat, ifedge.getDestination())) { + // exit condition identified + stat.setLooptype(DoStatement.LOOP_WHILE); + + // negate condition (while header) + IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy(); + ifexpr.negateIf(); + stat.setConditionExprent(ifexpr.getCondition()); + + // remove edges + firstif.getFirst().removeSuccessor(ifedge); + firstif.removeSuccessor(firstif.getAllSuccessorEdges().get(0)); + + if(stat.getAllSuccessorEdges().isEmpty()) { + ifedge.setSource(stat); + if(ifedge.closure == stat) { + ifedge.closure = stat.getParent(); + } + stat.addSuccessor(ifedge); + } + + // remove empty if statement as it is now part of the loop + if(firstif == stat.getFirst()) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList<Exprent>()); + stat.replaceStatement(firstif, bstat); + } else { + // precondition: sequence must contain more than one statement! + Statement sequence = firstif.getParent(); + sequence.getStats().removeWithKey(firstif.id); + sequence.setFirst(sequence.getStats().get(0)); + } + + return true; + } + } else { + StatEdge elseedge = firstif.getAllSuccessorEdges().get(0); + if(isDirectPath(stat, elseedge.getDestination())) { + // exit condition identified + stat.setLooptype(DoStatement.LOOP_WHILE); + + // no need to negate the while condition + stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition()); + + // remove edges + StatEdge ifedge = firstif.getIfEdge(); + firstif.getFirst().removeSuccessor(ifedge); + firstif.removeSuccessor(elseedge); + + if(stat.getAllSuccessorEdges().isEmpty()) { + + elseedge.setSource(stat); + if(elseedge.closure == stat) { + elseedge.closure = stat.getParent(); + } + stat.addSuccessor(elseedge); + } + + if(firstif.getIfstat() == null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList<Exprent>()); + + ifedge.setSource(bstat); + bstat.addSuccessor(ifedge); + + stat.replaceStatement(firstif, bstat); + } else { + // replace the if statement with its content + first.getParent().replaceStatement(first, firstif.getIfstat()); + + // lift closures + for(StatEdge prededge : elseedge.getDestination().getPredecessorEdges(StatEdge.TYPE_BREAK)) { + if(stat.containsStatementStrict(prededge.closure)) { + stat.addLabeledEdge(prededge); + } + } + + LabelHelper.lowClosures(stat); + } + + return true; + } + } + } + } + } + return false; + } + + public static boolean isDirectPath(Statement stat, Statement endstat) { + + Set<Statement> setStat = stat.getNeighboursSet(Statement.STATEDGE_DIRECT_ALL, Statement.DIRECTION_FORWARD); + if(setStat.isEmpty()) { + Statement parent = stat.getParent(); + if(parent == null) { + return false; + } else { + switch(parent.type) { + case Statement.TYPE_ROOT: + return endstat.type == Statement.TYPE_DUMMYEXIT; + case Statement.TYPE_DO: + return (endstat == parent); + case Statement.TYPE_SWITCH: + SwitchStatement swst = (SwitchStatement)parent; + for(int i=0;i<swst.getCaseStatements().size()-1;i++){ + Statement stt = swst.getCaseStatements().get(i); + if(stt == stat) { + Statement stnext = swst.getCaseStatements().get(i+1); + + if(stnext.getExprents() != null && stnext.getExprents().isEmpty()) { + stnext = stnext.getAllSuccessorEdges().get(0).getDestination(); + } + return (endstat == stnext); + } + } + default: + return isDirectPath(parent, endstat); + } + } + + } else { + return setStat.contains(endstat); + } + } + + private static boolean matchFor(DoStatement stat) { + + Exprent lastDoExprent = null, initDoExprent = null; + Statement lastData = null, preData = null; + + // get last exprent + lastData = getLastDirectData(stat.getFirst()); + if(lastData == null || lastData.getExprents().isEmpty()) { + return false; + } + + List<Exprent> lstExpr = lastData.getExprents(); + lastDoExprent = lstExpr.get(lstExpr.size()-1); + + boolean issingle = false; + if(lstExpr.size() == 1) { // single exprent + if(lastData.getAllPredecessorEdges().size() > 1) { // break edges + issingle = true; + } + } + + boolean haslast = issingle || (lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || + lastDoExprent.type == Exprent.EXPRENT_FUNCTION); + + if(!haslast) { + return false; + } + + boolean hasinit = false; + + // search for an initializing exprent + Statement current = stat; + for(;;){ + Statement parent = current.getParent(); + if(parent == null) { + break; + } + + if(parent.type == Statement.TYPE_SEQUENCE) { + if(current == parent.getFirst()) { + current = parent; + } else { + preData = current.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD).get(0); + preData = getLastDirectData(preData); + if(preData != null && !preData.getExprents().isEmpty()) { + initDoExprent = preData.getExprents().get(preData.getExprents().size()-1); + if(initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT) { + hasinit = true; + } + } + break; + } + } else { + break; + } + } + + if((hasinit && haslast) || issingle) { // FIXME: issingle sufficient? + + Set<Statement> set = stat.getNeighboursSet(StatEdge.TYPE_CONTINUE, Statement.DIRECTION_BACKWARD); + set.remove(lastData); + + if(!set.isEmpty()) { + return false; + } + + stat.setLooptype(DoStatement.LOOP_FOR); + if(hasinit) { + stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size()-1)); + } + stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size()-1)); + } + + if(lastData.getExprents().isEmpty()) { + List<StatEdge> lst = lastData.getAllSuccessorEdges(); + if(!lst.isEmpty()) { + lastData.removeSuccessor(lst.get(0)); + } + removeLastEmptyStatement(stat, lastData); + } + + return true; + } + + private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) { + + if(stat == dostat.getFirst()) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + bstat.setExprents(new ArrayList<Exprent>()); + dostat.replaceStatement(stat, bstat); + } else { + for(StatEdge edge: stat.getAllPredecessorEdges()) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_CONTINUE); + + stat.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, dostat); + dostat.addPredecessor(edge); + + dostat.addLabeledEdge(edge); + } + + // parent is a sequence statement + stat.getParent().getStats().removeWithKey(stat.id); + } + + } + + private static Statement getLastDirectData(Statement stat) { + + if(stat.getExprents() != null) { + return stat; + } + + switch(stat.type) { + case Statement.TYPE_SEQUENCE: + for(int i=stat.getStats().size()-1;i>=0;i--) { + Statement tmp = getLastDirectData(stat.getStats().get(i)); + if(tmp == null || !tmp.getExprents().isEmpty()) { + return tmp; + } + } + } + return null; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java new file mode 100644 index 0000000..be90625 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java @@ -0,0 +1,153 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class PPandMMHelper { + + private boolean exprentReplaced; + + public boolean findPPandMM(RootStatement root) { + + 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>(); + + boolean res = false; + + while(!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + + if(setVisited.contains(node)) { + continue; + } + setVisited.add(node); + + res |= processExprentList(node.exprents); + + stack.addAll(node.succs); + } + + return res; + } + + private boolean processExprentList(List<Exprent> lst) { + + boolean result = false; + + for(int i=0;i<lst.size();i++) { + Exprent exprent = lst.get(i); + exprentReplaced = false; + + Exprent retexpr = processExprentRecursive(exprent); + if(retexpr != null) { + lst.set(i, retexpr); + + result = true; + i--; // process the same exprent again + } + + result |= exprentReplaced; + } + + return result; + } + + private Exprent processExprentRecursive(Exprent exprent) { + + boolean replaced = true; + while(replaced) { + replaced = false; + + for(Exprent expr: exprent.getAllExprents()) { + Exprent retexpr = processExprentRecursive(expr); + if(retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + exprentReplaced = true; + break; + } + } + } + + if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)exprent; + + if(as.getRight().type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)as.getRight(); + + VarType midlayer = null; + if(func.getFunctype() >= FunctionExprent.FUNCTION_I2L && + func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + midlayer = func.getSimpleCastType(); + if(func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) { + func = (FunctionExprent)func.getLstOperands().get(0); + } else { + return null; + } + } + + if(func.getFunctype() == FunctionExprent.FUNCTION_ADD || + func.getFunctype() == FunctionExprent.FUNCTION_SUB) { + Exprent econd = func.getLstOperands().get(0); + Exprent econst = func.getLstOperands().get(1); + + if(econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && + func.getFunctype() == FunctionExprent.FUNCTION_ADD) { + econd = econst; + econst = func.getLstOperands().get(0); + } + + if(econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { + Exprent left = as.getLeft(); + + VarType condtype = econd.getExprType(); + if(left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) { + FunctionExprent ret = new FunctionExprent( + func.getFunctype() == FunctionExprent.FUNCTION_ADD?FunctionExprent.FUNCTION_PPI:FunctionExprent.FUNCTION_MMI, + Arrays.asList(new Exprent[]{econd})); + ret.setImplicitType(condtype); + + exprentReplaced = true; + return ret; + } + } + } + } + } + + return null; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java b/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java new file mode 100644 index 0000000..0a0ef45 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java @@ -0,0 +1,47 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; + +public class PrimitiveExprsList { + + private List<Exprent> lstExprents = new ArrayList<Exprent>(); + + private ExprentStack stack = new ExprentStack(); + + public PrimitiveExprsList() {} + + public PrimitiveExprsList copyStack() { + PrimitiveExprsList prlst = new PrimitiveExprsList(); + prlst.setStack(stack.clone()); + return prlst; + } + + public List<Exprent> getLstExprents() { + return lstExprents; + } + + public ExprentStack getStack() { + return stack; + } + + public void setStack(ExprentStack stack) { + this.stack = stack; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java new file mode 100644 index 0000000..e8f1e0a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -0,0 +1,428 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class SecondaryFunctionsHelper { + + private static final int[] funcsnot = new int[] { + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_COR, + FunctionExprent.FUNCTION_CADD + }; + + private static final HashMap<Integer, Integer[]> mapNumComparisons = new HashMap<Integer, Integer[]>(); + + static { + mapNumComparisons.put(FunctionExprent.FUNCTION_EQ, new Integer[] {FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_GT}); + mapNumComparisons.put(FunctionExprent.FUNCTION_NE, new Integer[] {FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LE}); + mapNumComparisons.put(FunctionExprent.FUNCTION_GT, new Integer[] {FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, null}); + mapNumComparisons.put(FunctionExprent.FUNCTION_GE, new Integer[] {null, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT}); + mapNumComparisons.put(FunctionExprent.FUNCTION_LT, new Integer[] {null, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE}); + mapNumComparisons.put(FunctionExprent.FUNCTION_LE, new Integer[] {FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null}); + } + + + public static boolean identifySecondaryFunctions(Statement stat) { + + if(stat.getExprents() == null) { + // if(){;}else{...} -> if(!){...} + if(stat.type == Statement.TYPE_IF) { + IfStatement ifelsestat = (IfStatement)stat; + Statement ifstat = ifelsestat.getIfstat(); + + if(ifelsestat.iftype == IfStatement.IFTYPE_IFELSE && ifstat.getExprents() != null && + ifstat.getExprents().isEmpty() && (ifstat.getAllSuccessorEdges().isEmpty() || !ifstat.getAllSuccessorEdges().get(0).explicit)) { + + // move else to the if position + ifelsestat.getStats().removeWithKey(ifstat.id); + + ifelsestat.iftype = IfStatement.IFTYPE_IF; + ifelsestat.setIfstat(ifelsestat.getElsestat()); + ifelsestat.setElsestat(null); + + if(ifelsestat.getAllSuccessorEdges().isEmpty() && !ifstat.getAllSuccessorEdges().isEmpty()) { + StatEdge endedge = ifstat.getAllSuccessorEdges().get(0); + + ifstat.removeSuccessor(endedge); + endedge.setSource(ifelsestat); + if(endedge.closure != null) { + ifelsestat.getParent().addLabeledEdge(endedge); + } + ifelsestat.addSuccessor(endedge); + } + + ifelsestat.getFirst().removeSuccessor(ifelsestat.getIfEdge()); + + ifelsestat.setIfEdge(ifelsestat.getElseEdge()); + ifelsestat.setElseEdge(null); + + // negate head expression + ifelsestat.setNegated(!ifelsestat.isNegated()); + ifelsestat.getHeadexprentList().set(0, ((IfExprent)ifelsestat.getHeadexprent().copy()).negateIf()); + + return true; + } + } + } + + + boolean replaced = true; + while(replaced) { + replaced = false; + + List<Object> lstObjects = new ArrayList<Object>(stat.getExprents()==null?stat.getSequentialObjects():stat.getExprents()); + + for(int i=0;i<lstObjects.size();i++) { + Object obj = lstObjects.get(i); + + if(obj instanceof Statement) { + if(identifySecondaryFunctions((Statement)obj)) { + replaced = true; + break; + } + } else if(obj instanceof Exprent) { + Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true); + if(retexpr != null) { + if(stat.getExprents()==null) { + // only head expressions can be replaced! + stat.replaceExprent((Exprent)obj, retexpr); + } else { + stat.getExprents().set(i, retexpr); + } + replaced = true; + break; + } + } + } + } + + return false; + } + + + private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level) { + + if(exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + + switch(fexpr.getFunctype()) { + case FunctionExprent.FUNCTION_BOOLNOT: + + Exprent retparam = propagateBoolNot(fexpr); + + if(retparam != null) { + return retparam; + } + + break; + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + case FunctionExprent.FUNCTION_GT: + case FunctionExprent.FUNCTION_GE: + case FunctionExprent.FUNCTION_LT: + case FunctionExprent.FUNCTION_LE: + Exprent expr1 = fexpr.getLstOperands().get(0); + Exprent expr2 = fexpr.getLstOperands().get(1); + + if(expr1.type == Exprent.EXPRENT_CONST) { + expr2 = expr1; + expr1 = fexpr.getLstOperands().get(1); + } + + if(expr1.type == Exprent.EXPRENT_FUNCTION && expr2.type == Exprent.EXPRENT_CONST) { + FunctionExprent funcexpr = (FunctionExprent)expr1; + ConstExprent cexpr = (ConstExprent)expr2; + + int functype = funcexpr.getFunctype(); + if(functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG || + functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG || + functype == FunctionExprent.FUNCTION_DCMPL) { + + int desttype = -1; + + Integer[] destcons = mapNumComparisons.get(fexpr.getFunctype()); + if(destcons != null) { + int index = cexpr.getIntValue()+1; + if(index >= 0 && index <= 2) { + Integer destcon = destcons[index]; + if(destcon != null) { + desttype = destcon.intValue(); + } + } + } + + if(desttype >= 0) { + return new FunctionExprent(desttype, funcexpr.getLstOperands()); + } + } + } + } + } + + + boolean replaced = true; + while(replaced) { + replaced = false; + + for(Exprent expr: exprent.getAllExprents()) { + Exprent retexpr = identifySecondaryFunctions(expr, false); + if(retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + break; + } + } + } + + switch(exprent.type) { + case Exprent.EXPRENT_FUNCTION: + FunctionExprent fexpr = (FunctionExprent)exprent; + List<Exprent> lstOperands = fexpr.getLstOperands(); + + switch(fexpr.getFunctype()) { + case FunctionExprent.FUNCTION_XOR: + for(int i=0;i<2;i++) { + Exprent operand = lstOperands.get(i); + VarType operandtype = operand.getExprType(); + + if(operand.type == Exprent.EXPRENT_CONST && + operandtype.type != CodeConstants.TYPE_BOOLEAN) { + ConstExprent cexpr = (ConstExprent)operand; + long val; + if(operandtype.type == CodeConstants.TYPE_LONG) { + val = ((Long)cexpr.getValue()).longValue(); + } else { + val = ((Integer)cexpr.getValue()).intValue(); + } + + if(val == -1) { + List<Exprent> lstBitNotOperand = new ArrayList<Exprent>(); + lstBitNotOperand.add(lstOperands.get(1-i)); + return new FunctionExprent(FunctionExprent.FUNCTION_BITNOT, lstBitNotOperand); + } + } + } + break; + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + if(lstOperands.get(0).getExprType().type == CodeConstants.TYPE_BOOLEAN && + lstOperands.get(1).getExprType().type == CodeConstants.TYPE_BOOLEAN) { + for(int i=0;i<2;i++) { + if(lstOperands.get(i).type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)lstOperands.get(i); + int val = ((Integer)cexpr.getValue()).intValue(); + + if((fexpr.getFunctype() == FunctionExprent.FUNCTION_EQ && val == 1) || + (fexpr.getFunctype() == FunctionExprent.FUNCTION_NE && val == 0)) { + return lstOperands.get(1-i); + } else { + List<Exprent> lstNotOperand = new ArrayList<Exprent>(); + lstNotOperand.add(lstOperands.get(1-i)); + return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstNotOperand); + } + } + } + } + break; + case FunctionExprent.FUNCTION_BOOLNOT: + if(lstOperands.get(0).type == Exprent.EXPRENT_CONST) { + int val = ((ConstExprent)lstOperands.get(0)).getIntValue(); + if(val == 0) { + return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(1)); + } else { + return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(0)); + } + } + break; + case FunctionExprent.FUNCTION_IIF: + Exprent expr1 = lstOperands.get(1); + Exprent expr2 = lstOperands.get(2); + + if(expr1.type == Exprent.EXPRENT_CONST && expr2.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr1 = (ConstExprent)expr1; + ConstExprent cexpr2 = (ConstExprent)expr2; + + if(cexpr1.getExprType().type == CodeConstants.TYPE_BOOLEAN && + cexpr2.getExprType().type == CodeConstants.TYPE_BOOLEAN) { + + if(cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) { + return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[] {lstOperands.get(0)})); + } else if(cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) { + return lstOperands.get(0); + } + } + } + break; + case FunctionExprent.FUNCTION_LCMP: + case FunctionExprent.FUNCTION_FCMPL: + case FunctionExprent.FUNCTION_FCMPG: + case FunctionExprent.FUNCTION_DCMPL: + case FunctionExprent.FUNCTION_DCMPG: + int var = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + VarType type = lstOperands.get(0).getExprType(); + VarProcessor processor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); + + FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[] { + new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new Exprent[] {new VarExprent(var, type, processor), + ConstExprent.getZeroConstant(type.type)})), + new ConstExprent(VarType.VARTYPE_INT, new Integer(-1)), + new ConstExprent(VarType.VARTYPE_INT, new Integer(1))})); + + FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(new Exprent[] { + new AssignmentExprent(new VarExprent(var, type, processor), new FunctionExprent(FunctionExprent.FUNCTION_SUB, + Arrays.asList(new Exprent[] {lstOperands.get(0), lstOperands.get(1)}))), + ConstExprent.getZeroConstant(type.type)})); + + processor.setVarType(new VarVersionPaar(var, 0), type); + + return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[] { + head, new ConstExprent(VarType.VARTYPE_INT, new Integer(0)), iff})); + } + break; + case Exprent.EXPRENT_ASSIGNMENT: // check for conditional assignment + AssignmentExprent asexpr = (AssignmentExprent)exprent; + Exprent right = asexpr.getRight(); + Exprent left = asexpr.getLeft(); + + if(right.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)right; + + VarType midlayer = null; + if(func.getFunctype() >= FunctionExprent.FUNCTION_I2L && + func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + right = func.getLstOperands().get(0); + midlayer = func.getSimpleCastType(); + if(right.type == Exprent.EXPRENT_FUNCTION) { + func = (FunctionExprent)right; + } else { + return null; + } + } + + List<Exprent> lstFuncOperands = func.getLstOperands(); + + Exprent cond = null; + + switch(func.getFunctype()) { + case FunctionExprent.FUNCTION_ADD: + case FunctionExprent.FUNCTION_AND: + case FunctionExprent.FUNCTION_OR: + case FunctionExprent.FUNCTION_XOR: + if(left.equals(lstFuncOperands.get(1))) { + cond = lstFuncOperands.get(0); + break; + } + case FunctionExprent.FUNCTION_SUB: + case FunctionExprent.FUNCTION_MUL: + case FunctionExprent.FUNCTION_DIV: + case FunctionExprent.FUNCTION_REM: + case FunctionExprent.FUNCTION_SHL: + case FunctionExprent.FUNCTION_SHR: + case FunctionExprent.FUNCTION_USHR: + if(left.equals(lstFuncOperands.get(0))) { + cond = lstFuncOperands.get(1); + } + } + + if(cond!=null && (midlayer == null || midlayer.equals(cond.getExprType()))) { + asexpr.setRight(cond); + asexpr.setCondtype(func.getFunctype()); + } + } + break; + case Exprent.EXPRENT_INVOCATION: + if(!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once). + Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent); + if(!exprent.equals(retexpr)) { + return retexpr; + } + } + } + + return null; + + } + + public static Exprent propagateBoolNot(Exprent exprent) { + + if(exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + + if(fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT) { + + Exprent param = fexpr.getLstOperands().get(0); + + if(param.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fparam = (FunctionExprent)param; + + int ftype = fparam.getFunctype(); + switch(ftype) { + case FunctionExprent.FUNCTION_BOOLNOT: + Exprent newexpr = fparam.getLstOperands().get(0); + Exprent retexpr = propagateBoolNot(newexpr); + return retexpr == null?newexpr:retexpr; + case FunctionExprent.FUNCTION_CADD: + case FunctionExprent.FUNCTION_COR: + List<Exprent> operands = fparam.getLstOperands(); + for(int i=0;i<operands.size();i++) { + Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{operands.get(i)})); + + Exprent retparam = propagateBoolNot(newparam); + operands.set(i, retparam == null?newparam:retparam); + } + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + case FunctionExprent.FUNCTION_LT: + case FunctionExprent.FUNCTION_GE: + case FunctionExprent.FUNCTION_GT: + case FunctionExprent.FUNCTION_LE: + fparam.setFunctype(funcsnot[ftype-FunctionExprent.FUNCTION_EQ]); + return fparam; + } + } + } + } + + return null; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java new file mode 100644 index 0000000..69721e8 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java @@ -0,0 +1,326 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class SequenceHelper { + + + public static void condenseSequences(Statement root) { + condenseSequencesRec(root); + } + + private static void condenseSequencesRec(Statement stat) { + + if(stat.type == Statement.TYPE_SEQUENCE) { + + List<Statement> lst = new ArrayList<Statement>(); + lst.addAll(stat.getStats()); + + boolean unfolded = false; + + // unfold blocks + for(int i=0;i<lst.size();i++) { + Statement st = lst.get(i); + if(st.type == Statement.TYPE_SEQUENCE) { + + removeEmptyStatements((SequenceStatement)st); + + if(i == lst.size()-1 || isSequenceDisbandable(st, lst.get(i+1))) { + // move predecessors + Statement first = st.getFirst(); + for(StatEdge edge: st.getAllPredecessorEdges()) { + st.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, first); + first.addPredecessor(edge); + } + + // move successors + Statement last = st.getStats().getLast(); + if(last.getAllSuccessorEdges().isEmpty() && i<lst.size()-1) { + last.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, last, lst.get(i+1))); + } else { + for(StatEdge edge: last.getAllSuccessorEdges()) { + if(i == lst.size()-1) { + if(edge.closure == st) { + stat.addLabeledEdge(edge); + } + } else { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); + edge.closure.getLabelEdges().remove(edge); + edge.closure = null; + } + } + } + + for(StatEdge edge : st.getAllSuccessorEdges()) { + st.removeSuccessor(edge); + } + + for(StatEdge edge: new HashSet<StatEdge>(st.getLabelEdges())) { + if(edge.getSource() != last) { + last.addLabeledEdge(edge); + } + } + + lst.remove(i); + lst.addAll(i, st.getStats()); + i--; + + unfolded = true; + } + } + } + + if(unfolded) { + SequenceStatement sequence = new SequenceStatement(lst); + sequence.setAllParent(); + + stat.getParent().replaceStatement(stat, sequence); + + stat = sequence; + } + } + + // sequence consisting of one statement -> disband + if(stat.type == Statement.TYPE_SEQUENCE) { + + removeEmptyStatements((SequenceStatement)stat); + + if(stat.getStats().size() == 1) { + + Statement st = stat.getFirst(); + + boolean ok = st.getAllSuccessorEdges().isEmpty(); + if(!ok) { + StatEdge edge = st.getAllSuccessorEdges().get(0); + + ok = stat.getAllSuccessorEdges().isEmpty(); + if(!ok) { + StatEdge statedge = stat.getAllSuccessorEdges().get(0); + ok = (edge.getDestination() == statedge.getDestination()); + + if(ok) { + st.removeSuccessor(edge); + } + } + } + + if(ok) { + stat.getParent().replaceStatement(stat, st); + stat = st; + } + } + } + + // replace flat statements with synthetic basic blocks + outer: + for(;;) { + for(Statement st: stat.getStats()) { + if((st.getStats().isEmpty() || st.getExprents() != null) && st.type != Statement.TYPE_BASICBLOCK) { + destroyAndFlattenStatement(st); + continue outer; + } + } + break; + } + + // recursion + for(int i=0;i<stat.getStats().size();i++) { + condenseSequencesRec(stat.getStats().get(i)); + } + + } + + private static boolean isSequenceDisbandable(Statement block, Statement next) { + + Statement last = block.getStats().getLast(); + List<StatEdge> lstSuccs = last.getAllSuccessorEdges(); + if(!lstSuccs.isEmpty()) { + if(lstSuccs.get(0).getDestination() != next) { + return false; + } + } + + for(StatEdge edge : next.getPredecessorEdges(StatEdge.TYPE_BREAK)) { + if(last != edge.getSource() && !last.containsStatementStrict(edge.getSource())) { + return false; + } + } + + return true; + } + + private static void removeEmptyStatements(SequenceStatement sequence) { + + if(sequence.getStats().size() <= 1) { + return; + } + + mergeFlatStatements(sequence); + + for(;;) { + + boolean found = false; + + for(Statement st: sequence.getStats()) { + + if(st.getExprents() != null && st.getExprents().isEmpty()) { + + if(st.getAllSuccessorEdges().isEmpty()) { + List<StatEdge> lstBreaks = st.getPredecessorEdges(StatEdge.TYPE_BREAK); + + if(lstBreaks.isEmpty()) { + for(StatEdge edge: st.getAllPredecessorEdges()) { + edge.getSource().removeSuccessor(edge); + } + found = true; + } + } else { + StatEdge sucedge = st.getAllSuccessorEdges().get(0); + if(sucedge.getType() != StatEdge.TYPE_FINALLYEXIT) { + st.removeSuccessor(sucedge); + + for(StatEdge edge: st.getAllPredecessorEdges()) { + if(sucedge.getType()!=StatEdge.TYPE_REGULAR) { + edge.getSource().changeEdgeType(Statement.DIRECTION_FORWARD, edge, sucedge.getType()); + } + + st.removePredecessor(edge); + edge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, edge, sucedge.getDestination()); + sucedge.getDestination().addPredecessor(edge); + + if(sucedge.closure != null) { + sucedge.closure.addLabeledEdge(edge); + } + } + found = true; + } + } + + if(found) { + sequence.getStats().removeWithKey(st.id); + break; + } + } + } + + if(!found) { + break; + } + } + + sequence.setFirst(sequence.getStats().get(0)); + + } + + private static void mergeFlatStatements(SequenceStatement sequence) { + + for(;;) { + + Statement next = null; + Statement current = null; + + boolean found = false; + + for(int i=sequence.getStats().size()-1;i>=0;i--) { + + next = current; + current = sequence.getStats().get(i); + + if(next != null && current.getExprents()!=null && !current.getExprents().isEmpty()) { + if(next.getExprents()!=null) { + next.getExprents().addAll(0, current.getExprents()); + current.getExprents().clear(); + found = true; + } else { + Statement first = getFirstExprentlist(next); + if(first != null) { + first.getExprents().addAll(0, current.getExprents()); + current.getExprents().clear(); + found = true; + } + } + } + } + + if(!found) { + break; + } + } + + } + + private static Statement getFirstExprentlist(Statement stat) { + + if(stat.getExprents() != null) { + return stat; + } + + switch(stat.type) { + case Statement.TYPE_IF: + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + return getFirstExprentlist(stat.getFirst()); + } + + return null; + } + + + public static void destroyAndFlattenStatement(Statement stat) { + + destroyStatementContent(stat, false); + + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + if(stat.getExprents() == null) { + bstat.setExprents(new ArrayList<Exprent>()); + } else { + bstat.setExprents(DecHelper.copyExprentList(stat.getExprents())); + } + + stat.getParent().replaceStatement(stat, bstat); + } + + public static void destroyStatementContent(Statement stat, boolean self) { + + for(Statement st: stat.getStats()) { + destroyStatementContent(st, true); + } + stat.getStats().clear(); + + if(self) { + for(StatEdge edge : stat.getAllSuccessorEdges()) { + stat.removeSuccessor(edge); + } + } + + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java new file mode 100644 index 0000000..8f55013 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java @@ -0,0 +1,863 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; + +public class SimplifyExprentsHelper { + + private boolean firstInvocation; + + public SimplifyExprentsHelper(boolean firstInvocation) { + this.firstInvocation = firstInvocation; + } + + public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) { + + boolean res = false; + + if(stat.getExprents() == null) { + + for(;;) { + + boolean changed = false; + + for(Statement st: stat.getStats()) { + res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl); + + // collapse composed if's + if(changed = IfHelper.mergeIfs(st, setReorderedIfs)) { + break; + } + + // collapse iff ?: statement + if(changed = buildIff(st, ssa)) { + break; + } + } + + res |= changed; + + if(!changed) { + break; + } + } + + } else { + res |= simplifyStackVarsExprents(stat.getExprents(), cl); + } + + return res; + } + + private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) { + + boolean res = false; + + int index = 0; + + while(index < list.size()) { + + Exprent current = list.get(index); + + Exprent ret = isSimpleConstructorInvocation(current); + if(ret != null) { + list.set(index, ret); + res = true; + + continue; + } + + // lambda expression (Java 8) + ret = isLambda(current, cl); + if(ret != null) { + list.set(index, ret); + res = true; + + continue; + } + + // remove monitor exit + if(isMonitorExit(current)) { + list.remove(index); + res = true; + + continue; + } + + // trivial assignment of a stack variable + if(isTrivialStackAssignment(current)) { + list.remove(index); + res = true; + + continue; + } + + if(index == list.size()-1) { + break; + } + + + Exprent next = list.get(index+1); + + + // constructor invocation + if(isConstructorInvocationRemote(list, index)) { + list.remove(index); + res = true; + + continue; + } + + // remove getClass() invocation, which is part of a qualified new + if(DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GETCLASS_NEW)) { + if(isQualifiedNewGetClass(current, next)) { + list.remove(index); + res = true; + + continue; + } + } + + // direct initialization of an array + int arrcount = isArrayInitializer(list, index); + if(arrcount > 0) { + for(int i=0;i<arrcount;i++) { + list.remove(index+1); + } + res = true; + + continue; + } + + // add array initializer expression + if(addArrayInitializer(current, next)) { + list.remove(index+1); + res = true; + + continue; + } + + // integer ++expr and --expr (except for vars!) + Exprent func = isPPIorMMI(current); + if(func != null) { + list.set(index, func); + res = true; + + continue; + } + + // expr++ and expr-- + if(isIPPorIMM(current, next)) { + list.remove(index+1); + res = true; + + continue; + } + + // assignment on stack + if(isStackAssignement(current, next)) { + list.remove(index+1); + res = true; + + continue; + } + + if(!firstInvocation && isStackAssignement2(current, next)) { + list.remove(index+1); + res = true; + + continue; + } + + index++; + } + + return res; + } + + private static boolean addArrayInitializer(Exprent first, Exprent second) { + + if(first.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)first; + + if(as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { + NewExprent newex = (NewExprent)as.getRight(); + + if(!newex.getLstArrayElements().isEmpty()) { + + VarExprent arrvar = (VarExprent)as.getLeft(); + + if(second.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent aas = (AssignmentExprent)second; + if(aas.getLeft().type == Exprent.EXPRENT_ARRAY) { + ArrayExprent arrex = (ArrayExprent)aas.getLeft(); + if(arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) + && arrex.getIndex().type == Exprent.EXPRENT_CONST) { + + int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue(); + + if(constvalue < newex.getLstArrayElements().size()) { + Exprent init = newex.getLstArrayElements().get(constvalue); + if(init.type == Exprent.EXPRENT_CONST) { + ConstExprent cinit = (ConstExprent)init; + + VarType arrtype = newex.getNewtype().copy(); + arrtype.decArrayDim(); + + ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); + + if(cinit.equals(defaultval)) { + + Exprent tempexpr = aas.getRight(); + + if(!tempexpr.containsExprent(arrvar)) { + newex.getLstArrayElements().set(constvalue, tempexpr); + + if(tempexpr.type == Exprent.EXPRENT_NEW) { + NewExprent tempnewex = (NewExprent)tempexpr; + int dims = newex.getNewtype().arraydim; + if(dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { + tempnewex.setDirectArrayInit(true); + } + } + + return true; + } + } + } + } + } + } + } + } + } + } + + return false; + } + + + + private static int isArrayInitializer(List<Exprent> list, int index) { + + Exprent current = list.get(index); + if(current.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)current; + + if(as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) { + NewExprent newex = (NewExprent)as.getRight(); + + if(newex.getExprType().arraydim > 0 && newex.getLstDims().size() == 1 && newex.getLstArrayElements().isEmpty() && + newex.getLstDims().get(0).type == Exprent.EXPRENT_CONST) { + + int size = ((Integer)((ConstExprent)newex.getLstDims().get(0)).getValue()).intValue(); + if(size == 0) { + return 0; + } + + VarExprent arrvar = (VarExprent)as.getLeft(); + + HashMap<Integer, Exprent> mapInit = new HashMap<Integer, Exprent>(); + + int i=1; + while(index+i < list.size() && i <= size) { + boolean found = false; + + Exprent expr = list.get(index+i); + if(expr.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent aas = (AssignmentExprent)expr; + if(aas.getLeft().type == Exprent.EXPRENT_ARRAY) { + ArrayExprent arrex = (ArrayExprent)aas.getLeft(); + if(arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray()) + && arrex.getIndex().type == Exprent.EXPRENT_CONST) { + + int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue(); // TODO: check for a number type. Failure extremely improbable, but nevertheless... + + if(constvalue < size && !mapInit.containsKey(constvalue)) { + + if(!aas.getRight().containsExprent(arrvar)) { + mapInit.put(constvalue, aas.getRight()); + found = true; + } + } + } + } + } + + if(!found) { + break; + } + + i++; + } + + double fraction = ((double)mapInit.size()) / size; + + if((arrvar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || + (size > 7 && fraction >= 0.7)) { + + List<Exprent> lstRet = new ArrayList<Exprent>(); + + VarType arrtype = newex.getNewtype().copy(); + arrtype.decArrayDim(); + + ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype); + + for(int j=0;j<size;j++) { + lstRet.add(defaultval.copy()); + } + + int dims = newex.getNewtype().arraydim; + for(Entry<Integer, Exprent> ent: mapInit.entrySet()) { + Exprent tempexpr = ent.getValue(); + lstRet.set(ent.getKey(), tempexpr); + + if(tempexpr.type == Exprent.EXPRENT_NEW) { + NewExprent tempnewex = (NewExprent)tempexpr; + if(dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) { + tempnewex.setDirectArrayInit(true); + } + } + } + + newex.setLstArrayElements(lstRet); + + return mapInit.size(); + } + } + } + } + + return 0; + } + + private static boolean isTrivialStackAssignment(Exprent first) { + + if(first.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asf = (AssignmentExprent)first; + + if(asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) { + VarExprent varleft = (VarExprent)asf.getLeft(); + VarExprent varright = (VarExprent)asf.getRight(); + + if(varleft.getIndex() == varright.getIndex() && varleft.isStack() && + varright.isStack()) { + return true; + } + } + } + + return false; + } + + private static boolean isStackAssignement2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation + + if(first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asf = (AssignmentExprent)first; + AssignmentExprent ass = (AssignmentExprent)second; + + if(asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR && + asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) { + if(ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) { + asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight())); + return true; + } + } + } + + return false; + } + + private static boolean isStackAssignement(Exprent first, Exprent second) { + + if(first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asf = (AssignmentExprent)first; + AssignmentExprent ass = (AssignmentExprent)second; + + for(;;) { + if(asf.getRight().equals(ass.getRight())) { + if((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) && + (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) { + + if(!ass.getLeft().containsExprent(asf.getLeft())) { + asf.setRight(ass); + return true; + } + } + } + if(asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) { + asf = (AssignmentExprent)asf.getRight(); + } else { + break; + } + } + + } + + return false; + } + + private static Exprent isPPIorMMI(Exprent first) { + + if(first.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)first; + + if(as.getRight().type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)as.getRight(); + + if(func.getFunctype() == FunctionExprent.FUNCTION_ADD || + func.getFunctype() == FunctionExprent.FUNCTION_SUB) { + Exprent econd = func.getLstOperands().get(0); + Exprent econst = func.getLstOperands().get(1); + + if(econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && + func.getFunctype() == FunctionExprent.FUNCTION_ADD) { + econd = econst; + econst = func.getLstOperands().get(0); + } + + if(econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) { + Exprent left = as.getLeft(); + + if(left.type != Exprent.EXPRENT_VAR && left.equals(econd)) { + FunctionExprent ret = new FunctionExprent( + func.getFunctype() == FunctionExprent.FUNCTION_ADD?FunctionExprent.FUNCTION_PPI:FunctionExprent.FUNCTION_MMI, + Arrays.asList(new Exprent[]{econd})); + ret.setImplicitType(VarType.VARTYPE_INT); + return ret; + } + } + } + } + } + + return null; + } + + private static boolean isIPPorIMM(Exprent first, Exprent second) { + + if(first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) { + AssignmentExprent as = (AssignmentExprent)first; + FunctionExprent in = (FunctionExprent)second; + + if((in.getFunctype() == FunctionExprent.FUNCTION_MMI || in.getFunctype() == FunctionExprent.FUNCTION_PPI) && + in.getLstOperands().get(0).equals(as.getRight())) { + + if(in.getFunctype() == FunctionExprent.FUNCTION_MMI) { + in.setFunctype(FunctionExprent.FUNCTION_IMM); + } else { + in.setFunctype(FunctionExprent.FUNCTION_IPP); + } + as.setRight(in); + + return true; + } + } + + return false; + } + + private static boolean isMonitorExit(Exprent first) { + if(first.type == Exprent.EXPRENT_MONITOR) { + MonitorExprent monexpr = (MonitorExprent)first; + if(monexpr.getMontype() == MonitorExprent.MONITOR_EXIT && monexpr.getValue().type == Exprent.EXPRENT_VAR + && !((VarExprent)monexpr.getValue()).isStack()) { + return true; + } + } + + return false; + } + + private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) { + + if(first.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)first; + + if(!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR && invexpr.getName().equals("getClass") && + invexpr.getStringDescriptor().equals("()Ljava/lang/Class;")) { + + List<Exprent> lstExprents = second.getAllExprents(); + lstExprents.add(second); + + for(Exprent expr : lstExprents) { + if(expr.type == Exprent.EXPRENT_NEW) { + NewExprent nexpr = (NewExprent)expr; + if(nexpr.getConstructor() != null && !nexpr.getConstructor().getLstParameters().isEmpty() && + nexpr.getConstructor().getLstParameters().get(0).equals(invexpr.getInstance())) { + + String classname = nexpr.getNewtype().value; + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); + if(node != null && node.type != ClassNode.CLASS_ROOT) { + return true; + } + } + } + } + + } + } + + return false; + } + +// private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) { +// +// Exprent current = list.get(index); +// +// if(current.type == Exprent.EXPRENT_ASSIGNMENT) { +// AssignmentExprent as = (AssignmentExprent)current; +// +// if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { +// +// NewExprent newexpr = (NewExprent)as.getRight(); +// VarType newtype = newexpr.getNewtype(); +// VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); +// +// if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && +// newexpr.getConstructor() == null) { +// +// Set<VarVersionPaar> setChangedVars = new HashSet<VarVersionPaar>(); +// +// for(int i = index + 1; i < list.size(); i++) { +// Exprent remote = list.get(i); +// +// if(remote.type == Exprent.EXPRENT_INVOCATION) { +// InvocationExprent in = (InvocationExprent)remote; +// +// if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR +// && as.getLeft().equals(in.getInstance())) { +// +// Set<VarVersionPaar> setVars = remote.getAllVariables(); +// setVars.remove(leftPaar); +// setVars.retainAll(setChangedVars); +// +// if(setVars.isEmpty()) { +// +// newexpr.setConstructor(in); +// in.setInstance(null); +// +// if(!setChangedVars.isEmpty()) { // some exprents inbetween +// list.add(index+1, as.copy()); +// list.remove(i+1); +// } else { +// list.set(i, as.copy()); +// } +// +// return true; +// } +// } +// } +// +// boolean isTempAssignment = false; +// +// if(remote.type == Exprent.EXPRENT_ASSIGNMENT) { // ugly solution +// AssignmentExprent asremote = (AssignmentExprent)remote; +// if(asremote.getLeft().type == Exprent.EXPRENT_VAR && +// asremote.getRight().type == Exprent.EXPRENT_VAR) { +// setChangedVars.add(new VarVersionPaar((VarExprent)asremote.getLeft())); +// isTempAssignment = true; +// } +// +// // FIXME: needs to be rewritten +// // propagate (var = new X) forward to the <init> invokation and then reduce +// +//// if(asremote.getLeft().type == Exprent.EXPRENT_VAR) { +//// List<Exprent> lstRightExprents = asremote.getRight().getAllExprents(true); +//// lstRightExprents.add(asremote.getRight()); +//// +//// Set<VarVersionPaar> setTempChangedVars = new HashSet<VarVersionPaar>(); +//// boolean isTemp = true; +//// +//// for(Exprent expr : lstRightExprents) { +//// if(expr.type != Exprent.EXPRENT_VAR && expr.type != Exprent.EXPRENT_FIELD) { +//// isTemp = false; +//// break; +//// } else if(expr.type == Exprent.EXPRENT_VAR) { +//// setTempChangedVars.add(new VarVersionPaar((VarExprent)expr)); +//// } +//// } +//// +//// if(isTemp) { +//// setChangedVars.addAll(setTempChangedVars); +//// isTempAssignment = true; +//// } +//// } +//// } else if(remote.type == Exprent.EXPRENT_FUNCTION) { +//// FunctionExprent fexpr = (FunctionExprent)remote; +//// if(fexpr.getFunctype() == FunctionExprent.FUNCTION_IPP || fexpr.getFunctype() == FunctionExprent.FUNCTION_IMM +//// || fexpr.getFunctype() == FunctionExprent.FUNCTION_PPI || fexpr.getFunctype() == FunctionExprent.FUNCTION_MMI) { +//// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { +//// setChangedVars.add(new VarVersionPaar((VarExprent)fexpr.getLstOperands().get(0))); +//// isTempAssignment = true; +//// } +//// } +// } +// +// if(!isTempAssignment) { +// Set<VarVersionPaar> setVars = remote.getAllVariables(); +// if(setVars.contains(leftPaar)) { +// return false; +// } else { +// setChangedVars.addAll(setVars); +// } +// } +// } +// } +// } +// } +// +// return false; +// } + + // propagate (var = new X) forward to the <init> invokation + private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) { + + Exprent current = list.get(index); + + if(current.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)current; + + if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) { + + NewExprent newexpr = (NewExprent)as.getRight(); + VarType newtype = newexpr.getNewtype(); + VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft()); + + if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0 && newexpr.getConstructor() == null) { + + for(int i = index + 1; i < list.size(); i++) { + Exprent remote = list.get(i); + + // <init> invocation + if(remote.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent in = (InvocationExprent)remote; + + if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR && as.getLeft().equals(in.getInstance())) { + + newexpr.setConstructor(in); + in.setInstance(null); + + list.set(i, as.copy()); + + return true; + } + } + + // check for variable in use + Set<VarVersionPaar> setVars = remote.getAllVariables(); + if(setVars.contains(leftPaar)) { // variable used somewhere in between -> exit, need a better reduced code + return false; + } + } + } + } + } + + return false; + } + + private static Exprent isLambda(Exprent exprent, StructClass cl) { + + List<Exprent> lst = exprent.getAllExprents(); + for(Exprent expr: lst) { + Exprent ret = isLambda(expr, cl); + if(ret != null) { + exprent.replaceExprent(expr, ret); + } + } + + if(exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent in = (InvocationExprent)exprent; + + if(in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) { + + String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix(); + ClassNode lambda_class = DecompilerContext.getClassprocessor().getMapRootClasses().get(lambda_class_name); + + if(lambda_class != null) { // real lambda class found, replace invocation with an anonymous class + + NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0); + newexp.setConstructor(in); + // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation + // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);) + // in this case instance will hold the corresponding object + + return newexp; + } + } + } + + return null; + } + + + private static Exprent isSimpleConstructorInvocation(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(); + for(Exprent expr: lst) { + Exprent ret = isSimpleConstructorInvocation(expr); + if(ret != null) { + exprent.replaceExprent(expr, ret); + } + } + + if(exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent in = (InvocationExprent)exprent; + if(in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) { + NewExprent newexp = (NewExprent)in.getInstance(); + newexp.setConstructor(in); + in.setInstance(null); + return newexp; + } + } + + return null; + } + + + private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) { + + if(stat.type == Statement.TYPE_IF && stat.getExprents() == null) { + IfStatement stif = (IfStatement)stat; + if(stif.iftype == IfStatement.IFTYPE_IFELSE) { + Statement ifstat = stif.getIfstat(); + Statement elsestat = stif.getElsestat(); + + if(ifstat.getExprents() != null && ifstat.getExprents().size() == 1 + && elsestat.getExprents()!= null && elsestat.getExprents().size() == 1 + && ifstat.getAllSuccessorEdges().size() == 1 && elsestat.getAllSuccessorEdges().size() == 1 + && ifstat.getAllSuccessorEdges().get(0).getDestination() == elsestat.getAllSuccessorEdges().get(0).getDestination()) { + + Exprent ifexpr = ifstat.getExprents().get(0); + Exprent elseexpr = elsestat.getExprents().get(0); + + if(ifexpr.type == Exprent.EXPRENT_ASSIGNMENT && elseexpr.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent ifas = (AssignmentExprent)ifexpr; + AssignmentExprent elseas = (AssignmentExprent)elseexpr; + + if(ifas.getLeft().type == Exprent.EXPRENT_VAR && elseas.getLeft().type == Exprent.EXPRENT_VAR) { + VarExprent ifvar = (VarExprent)ifas.getLeft(); + VarExprent elsevar = (VarExprent)elseas.getLeft(); + + if(ifvar.getIndex() == elsevar.getIndex() && ifvar.isStack()) { // ifvar.getIndex() >= VarExprent.STACK_BASE) { + + boolean found = false; + + for(Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) { + if(ent.getKey().var == ifvar.getIndex()) { + if(ent.getValue().contains(ifvar.getVersion()) && ent.getValue().contains(elsevar.getVersion())) { + found = true; + break; + } + } + } + + if(found) { + List<Exprent> data = new ArrayList<Exprent>(); + data.addAll(stif.getFirst().getExprents()); + + data.add(new AssignmentExprent(ifvar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, + Arrays.asList(new Exprent[] {((IfExprent)stif.getHeadexprent()).getCondition(), + ifas.getRight(), + elseas.getRight()})))); + stif.setExprents(data); + + if(stif.getAllSuccessorEdges().isEmpty()) { + StatEdge ifedge = ifstat.getAllSuccessorEdges().get(0); + StatEdge edge = new StatEdge(ifedge.getType(), stif, ifedge.getDestination()); + + stif.addSuccessor(edge); + if(ifedge.closure != null) { + ifedge.closure.addLabeledEdge(edge); + } + } + + SequenceHelper.destroyAndFlattenStatement(stif); + + return true; + } + } + } + } else if(ifexpr.type == Exprent.EXPRENT_EXIT && elseexpr.type == Exprent.EXPRENT_EXIT) { + ExitExprent ifex = (ExitExprent)ifexpr; + ExitExprent elseex = (ExitExprent)elseexpr; + + if(ifex.getExittype() == elseex.getExittype() && ifex.getValue() != null && elseex.getValue() != null && + ifex.getExittype() == ExitExprent.EXIT_RETURN) { + + // throw is dangerous, because of implicit casting to a common superclass + // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work + if(ifex.getExittype() == ExitExprent.EXIT_THROW && + !ifex.getValue().getExprType().equals(elseex.getValue().getExprType())) { // note: getExprType unreliable at this point! + return false; + } + + List<Exprent> data = new ArrayList<Exprent>(); + data.addAll(stif.getFirst().getExprents()); + + data.add(new ExitExprent(ifex.getExittype(), new FunctionExprent(FunctionExprent.FUNCTION_IIF, + Arrays.asList(new Exprent[] {((IfExprent)stif.getHeadexprent()).getCondition(), + ifex.getValue(), + elseex.getValue()})), ifex.getRettype())); + stif.setExprents(data); + + StatEdge retedge = ifstat.getAllSuccessorEdges().get(0); + stif.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, stif, retedge.getDestination(), retedge.closure==stif?stif.getParent():retedge.closure)); + + SequenceHelper.destroyAndFlattenStatement(stif); + + return true; + } + } + } + } + } + + return false; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java new file mode 100644 index 0000000..5dae30c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java @@ -0,0 +1,716 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAUConstructorSparseEx; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; + + +public class StackVarsProcessor { + + public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) { + + HashSet<Integer> setReorderedIfs = new HashSet<Integer>(); + + SSAUConstructorSparseEx ssau = null; + + for(;;) { + + boolean found = false; + +// System.out.println("--------------- \r\n"+root.toJava()); + + SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); + ssa.splitVariables(root, mt); + +// System.out.println("--------------- \r\n"+root.toJava()); + + + SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null); + while(sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) { +// System.out.println("--------------- \r\n"+root.toJava()); + found = true; + } + + +// System.out.println("=============== \r\n"+root.toJava()); + + setVersionsToNull(root); + + SequenceHelper.condenseSequences(root); + + ssau = new SSAUConstructorSparseEx(); + ssau.splitVariables(root, mt); + +// try { +// DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); +// } catch(Exception ex) { +// ex.printStackTrace(); +// } + +// System.out.println("++++++++++++++++ \r\n"+root.toJava()); + + + if(iterateStatements(root, ssau)) { + found = true; + } + +// System.out.println("***************** \r\n"+root.toJava()); + + setVersionsToNull(root); + + if(!found) { + break; + } + } + + // remove unused assignments + ssau = new SSAUConstructorSparseEx(); + ssau.splitVariables(root, mt); + +// try { +// DotExporter.toDotFile(ssau.getSsuversions(), new File("c:\\Temp\\gr12_my.dot")); +// } catch(Exception ex) { +// ex.printStackTrace(); +// } + + iterateStatements(root, ssau); + +// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + setVersionsToNull(root); + } + + private void setVersionsToNull(Statement stat) { + + if(stat.getExprents() == null) { + for(Object obj: stat.getSequentialObjects()) { + if(obj instanceof Statement) { + setVersionsToNull((Statement)obj); + } else if(obj instanceof Exprent) { + setExprentVersionsToNull((Exprent)obj); + } + } + } else { + for(Exprent exprent: stat.getExprents()) { + setExprentVersionsToNull(exprent); + } + } + } + + private void setExprentVersionsToNull(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for(Exprent expr: lst) { + if(expr.type == Exprent.EXPRENT_VAR) { + ((VarExprent)expr).setVersion(0); + } + } + } + + + private boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + boolean res = false; + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + LinkedList<HashMap<VarVersionPaar, Exprent>> stackMaps = new LinkedList<HashMap<VarVersionPaar, Exprent>>(); + + stack.add(dgraph.first); + stackMaps.add(new HashMap<VarVersionPaar, Exprent>()); + + while(!stack.isEmpty()) { + + DirectNode nd = stack.removeFirst(); + HashMap<VarVersionPaar, Exprent> mapVarValues = stackMaps.removeFirst(); + + if(setVisited.contains(nd)) { + continue; + } + setVisited.add(nd); + + List<List<Exprent>> lstLists = new ArrayList<List<Exprent>>(); + + if(!nd.exprents.isEmpty()) { + lstLists.add(nd.exprents); + } + + if(nd.succs.size() == 1){ + DirectNode ndsucc = nd.succs.get(0); + if(ndsucc.type == DirectNode.NODE_TAIL && !ndsucc.exprents.isEmpty()) { + lstLists.add(nd.succs.get(0).exprents); + nd = ndsucc; + } + } + + for(int i=0;i<lstLists.size();i++) { + List<Exprent> lst = lstLists.get(i); + + int index = 0; + while(index < lst.size()) { + Exprent next = null; + if(index == lst.size()-1) { + if(i<lstLists.size()-1) { + next = lstLists.get(i+1).get(0); + } + } else { + next = lst.get(index+1); + } + + int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa); + + //System.out.println("***************** \r\n"+root.toJava()); + + if(ret[0] >= 0) { + index = ret[0]; + } else { + index++; + } + res |= (ret[1] == 1); + } + } + + for(DirectNode ndx: nd.succs) { + stack.add(ndx); + stackMaps.add(new HashMap<VarVersionPaar, Exprent>(mapVarValues)); + } + + // make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty + // change loop type if necessary + if(nd.exprents.isEmpty() && + (nd.type == DirectNode.NODE_INIT || nd.type == DirectNode.NODE_CONDITION || nd.type == DirectNode.NODE_INCREMENT)) { + nd.exprents.add(null); + + if(nd.statement.type == Statement.TYPE_DO) { + DoStatement loop = (DoStatement)nd.statement; + + if(loop.getLooptype() == DoStatement.LOOP_FOR && loop.getInitExprent() == null && loop.getIncExprent() == null) { // "downgrade" loop to 'while' + loop.setLooptype(DoStatement.LOOP_WHILE); + } + } + } + } + + return res; + } + + + private Exprent isReplaceableVar(Exprent exprent, HashMap<VarVersionPaar, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { + + Exprent dest = null; + + if(exprent.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)exprent; + dest = mapVarValues.get(new VarVersionPaar(var)); + } + + return dest; + } + + private void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) { + + parent.replaceExprent(var, dest); + + // live sets + SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPaar(var)); + HashSet<VarVersionPaar> setVars = getAllVersions(dest); + + for(VarVersionPaar varpaar : setVars) { + VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar); + + for(Iterator<Entry<Integer, FastSparseSet<Integer>>> itent = node.live.entryList().iterator();itent.hasNext();) { + Entry<Integer, FastSparseSet<Integer>> ent = itent.next(); + + Integer key = ent.getKey(); + + if(!livemap.containsKey(key)) { + itent.remove(); + } else { + FastSparseSet<Integer> set = ent.getValue(); + + set.complement(livemap.get(key)); + if(set.isEmpty()) { + itent.remove(); + } + } + } + } + } + + private int[] iterateExprent(List<Exprent> lstExprents, int index, Exprent next, HashMap<VarVersionPaar, + Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { + + Exprent exprent = lstExprents.get(index); + + int changed = 0; + + for(Exprent expr: exprent.getAllExprents()) { + for(;;) { + Object[] arr = iterateChildExprent(expr, exprent, next, mapVarValues, ssau); + Exprent retexpr = (Exprent)arr[0]; + changed |= (Boolean)arr[1]?1:0; + + boolean isReplaceable = (Boolean)arr[2]; + if(retexpr != null) { + if(isReplaceable) { + replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau); + expr = retexpr; + } else { + exprent.replaceExprent(expr, retexpr); + } + changed = 1; + } + + if(!isReplaceable) { + break; + } + } + } + + // no var on the highest level, so no replacing + + VarExprent left = null; + Exprent right = null; + + if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)exprent; + if(as.getLeft().type == Exprent.EXPRENT_VAR) { + left = (VarExprent)as.getLeft(); + right = as.getRight(); + } + } + + if(left == null) { + return new int[]{-1, changed}; + } + + VarVersionPaar leftpaar = new VarVersionPaar(left); + + List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>(); + boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); + + if(!notdom && usedVers.isEmpty()) { + if(left.isStack() && (right.type == Exprent.EXPRENT_INVOCATION || + right.type == Exprent.EXPRENT_ASSIGNMENT || right.type == Exprent.EXPRENT_NEW)) { + if(right.type == Exprent.EXPRENT_NEW) { + // new Object(); permitted + NewExprent nexpr = (NewExprent)right; + if(nexpr.isAnonymous() || nexpr.getNewtype().arraydim > 0 + || nexpr.getNewtype().type != CodeConstants.TYPE_OBJECT) { + return new int[]{-1, changed}; + } + } + + lstExprents.set(index, right); + return new int[]{index+1, 1}; + } else if(right.type == Exprent.EXPRENT_VAR) { + lstExprents.remove(index); + return new int[]{index, 1}; + } else { + return new int[]{-1, changed}; + } + } + + int useflags = right.getExprentUse(); + + // stack variables only + if(!left.isStack() && + (right.type != Exprent.EXPRENT_VAR || ((VarExprent)right).isStack())) { // special case catch(... ex) + return new int[]{-1, changed}; + } + + if((useflags & Exprent.MULTIPLE_USES) == 0 && (notdom || usedVers.size()>1)) { + return new int[]{-1, changed}; + } + + HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau); + + boolean isSelfReference = mapVars.containsKey(leftpaar.var); + if(isSelfReference && notdom) { + return new int[]{-1, changed}; + } + + HashSet<VarVersionPaar> setNextVars = next==null?null:getAllVersions(next); + + // FIXME: fix the entire method! + if(right.type != Exprent.EXPRENT_CONST && right.type != Exprent.EXPRENT_VAR && setNextVars!=null && mapVars.containsKey(leftpaar.var)) { + for(VarVersionNode usedvar: usedVers) { + if(!setNextVars.contains(new VarVersionPaar(usedvar.var, usedvar.version))) { + return new int[]{-1, changed}; + } + } + } + + mapVars.remove(leftpaar.var); + + boolean vernotreplaced = false; + boolean verreplaced = false; + + + HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>(); + + for(VarVersionNode usedvar: usedVers) { + VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); + if(isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && + (right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD + || setNextVars==null || setNextVars.contains(usedver))) { + + setTempUsedVers.add(usedver); + verreplaced = true; + } else { + vernotreplaced = true; + } + } + + if(isSelfReference && vernotreplaced) { + return new int[]{-1, changed}; + } else { + for(VarVersionPaar usedver: setTempUsedVers) { + Exprent copy = right.copy(); + if(right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { + ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); + } + + mapVarValues.put(usedver, copy); + } + } + + if(!notdom && !vernotreplaced) { + // remove assignment + lstExprents.remove(index); + return new int[]{index, 1}; + } else if(verreplaced){ + return new int[]{index+1, changed}; + } else { + return new int[]{-1, changed}; + } + } + + private HashSet<VarVersionPaar> getAllVersions(Exprent exprent) { + + HashSet<VarVersionPaar> res = new HashSet<VarVersionPaar>(); + + List<Exprent> listTemp = new ArrayList<Exprent>(exprent.getAllExprents(true)); + listTemp.add(exprent); + + for(Exprent expr: listTemp) { + if(expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + res.add(new VarVersionPaar(var)); + } + } + + return res; + } + + private Object[] iterateChildExprent(Exprent exprent, Exprent parent, Exprent next, HashMap<VarVersionPaar, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { + + boolean changed = false; + + for(Exprent expr: exprent.getAllExprents()) { + for(;;) { + Object[] arr = iterateChildExprent(expr, parent, next, mapVarValues, ssau); + Exprent retexpr = (Exprent)arr[0]; + changed |= (Boolean)arr[1]; + + boolean isReplaceable = (Boolean)arr[2]; + if(retexpr != null) { + if(isReplaceable) { + replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau); + expr = retexpr; + } else { + exprent.replaceExprent(expr, retexpr); + } + changed = true; + } + + if(!isReplaceable) { + break; + } + } + } + + Exprent dest = isReplaceableVar(exprent, mapVarValues, ssau); + if(dest != null) { + return new Object[]{dest, true, true}; + } + + + VarExprent left = null; + Exprent right = null; + + if(exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent as = (AssignmentExprent)exprent; + if(as.getLeft().type == Exprent.EXPRENT_VAR) { + left = (VarExprent)as.getLeft(); + right = as.getRight(); + } + } + + if(left == null) { + return new Object[]{null, changed, false}; + } + + boolean isHeadSynchronized = false; + if(next == null && parent.type == Exprent.EXPRENT_MONITOR) { + MonitorExprent monexpr = (MonitorExprent)parent; + if(monexpr.getMontype() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) { + isHeadSynchronized = true; + } + } + + // stack variable or synchronized head exprent + if(!left.isStack() && !isHeadSynchronized) { + return new Object[]{null, changed, false}; + } + + VarVersionPaar leftpaar = new VarVersionPaar(left); + + List<VarVersionNode> usedVers = new ArrayList<VarVersionNode>(); + boolean notdom = getUsedVersions(ssau, leftpaar, usedVers); + + if(!notdom && usedVers.isEmpty()) { + return new Object[]{right, changed, false}; + } + + // stack variables only + if(!left.isStack()) { + return new Object[]{null, changed, false}; + } + + int useflags = right.getExprentUse(); + + if((useflags & Exprent.BOTH_FLAGS) != Exprent.BOTH_FLAGS) { + return new Object[]{null, changed, false}; + } + + HashMap<Integer, HashSet<VarVersionPaar>> mapVars = getAllVarVersions(leftpaar, right, ssau); + + if(mapVars.containsKey(leftpaar.var) && notdom) { + return new Object[]{null, changed, false}; + } + + + mapVars.remove(leftpaar.var); + + HashSet<VarVersionPaar> setAllowedVars = getAllVersions(parent); + if(next != null) { + setAllowedVars.addAll(getAllVersions(next)); + } + + boolean vernotreplaced = false; + + HashSet<VarVersionPaar> setTempUsedVers = new HashSet<VarVersionPaar>(); + + for(VarVersionNode usedvar: usedVers) { + VarVersionPaar usedver = new VarVersionPaar(usedvar.var, usedvar.version); + if(isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && + (right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) { + + setTempUsedVers.add(usedver); + } else { + vernotreplaced = true; + } + } + + if(!notdom && !vernotreplaced) { + + for(VarVersionPaar usedver: setTempUsedVers) { + Exprent copy = right.copy(); + if(right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) { + ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id)); + } + + mapVarValues.put(usedver, copy); + } + + // remove assignment + return new Object[]{right, changed, false}; + } + + return new Object[]{null, changed, false}; + } + + private boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPaar var, List<VarVersionNode> res) { + + VarVersionsGraph ssuversions = ssa.getSsuversions(); + VarVersionNode varnode = ssuversions.nodes.getWithKey(var); + + HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>(); + + HashSet<VarVersionNode> setNotDoms = new HashSet<VarVersionNode>(); + + LinkedList<VarVersionNode> stack = new LinkedList<VarVersionNode>(); + stack.add(varnode); + + while(!stack.isEmpty()) { + + VarVersionNode nd = stack.remove(0); + setVisited.add(nd); + + if(nd != varnode && (nd.flags & VarVersionNode.FLAG_PHANTOM_FINEXIT)==0) { + res.add(nd); + } + + for(VarVersionEdge edge: nd.succs) { + VarVersionNode succ = edge.dest; + + if(!setVisited.contains(edge.dest)) { + + boolean isDominated = true; + for(VarVersionEdge prededge : succ.preds) { + if(!setVisited.contains(prededge.source)) { + isDominated = false; + break; + } + } + + if(isDominated) { + stack.add(succ); + } else { + setNotDoms.add(succ); + } + } + } + } + + setNotDoms.removeAll(setVisited); + + return !setNotDoms.isEmpty(); + } + + private boolean isVersionToBeReplaced(VarVersionPaar usedvar, HashMap<Integer, HashSet<VarVersionPaar>> mapVars, SSAUConstructorSparseEx ssau, VarVersionPaar leftpaar) { + + VarVersionsGraph ssuversions = ssau.getSsuversions(); + + SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar); + if(mapLiveVars == null) { + // dummy version, predecessor of a phi node + return false; + } + + // compare protected ranges + if(!InterpreterUtil.equalObjects(ssau.getMapVersionFirstRange().get(leftpaar), + ssau.getMapVersionFirstRange().get(usedvar))) { + return false; + } + + for(Entry<Integer, HashSet<VarVersionPaar>> ent: mapVars.entrySet()) { + FastSparseSet<Integer> liveverset = mapLiveVars.get(ent.getKey()); + if(liveverset == null) { + return false; + } + + HashSet<VarVersionNode> domset = new HashSet<VarVersionNode>(); + for(VarVersionPaar verpaar: ent.getValue()) { + domset.add(ssuversions.nodes.getWithKey(verpaar)); + } + + boolean isdom = false; + + for(Integer livever: liveverset) { + VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPaar(ent.getKey().intValue(), livever.intValue())); + + if(ssuversions.isDominatorSet(node, domset)) { + isdom = true; + break; + } + } + + if(!isdom) { + return false; + } + } + + return true; + } + + private HashMap<Integer, HashSet<VarVersionPaar>> getAllVarVersions(VarVersionPaar leftvar, Exprent exprent, SSAUConstructorSparseEx ssau) { + + HashMap<Integer, HashSet<VarVersionPaar>> map = new HashMap<Integer, HashSet<VarVersionPaar>>(); + SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar); + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for(Exprent expr: lst) { + if(expr.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent)expr).getIndex(); + if(leftvar.var != varindex) { + if(mapLiveVars.containsKey(varindex)) { + HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>(); + for(Integer vers: mapLiveVars.get(varindex)) { + verset.add(new VarVersionPaar(varindex, vers.intValue())); + } + map.put(varindex, verset); + } else { + throw new RuntimeException("inkonsistent live map!"); + } + } else { + map.put(varindex, null); + } + } else if(expr.type == Exprent.EXPRENT_FIELD) { + if(ssau.getMapFieldVars().containsKey(expr.id)) { + int varindex = ssau.getMapFieldVars().get(expr.id); + if(mapLiveVars.containsKey(varindex)) { + HashSet<VarVersionPaar> verset = new HashSet<VarVersionPaar>(); + for(Integer vers: mapLiveVars.get(varindex)) { + verset.add(new VarVersionPaar(varindex, vers.intValue())); + } + map.put(varindex, verset); + } + } + } + } + + return map; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java new file mode 100644 index 0000000..b20c2c7 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java @@ -0,0 +1,103 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + +public class StatEdge { + + public static final int TYPE_ALL = 0xFF; + + public static final int TYPE_REGULAR = 1; + public static final int TYPE_EXCEPTION = 2; + public static final int TYPE_BREAK = 4; + public static final int TYPE_CONTINUE = 8; + public static final int TYPE_FINALLYEXIT = 32; + + public static final int[] TYPES = new int[] { + TYPE_REGULAR, + TYPE_EXCEPTION, + TYPE_BREAK, + TYPE_CONTINUE, + TYPE_FINALLYEXIT + }; + + private int type; + + private Statement source; + + private Statement destination; + + private List<String> exceptions; + + public Statement closure; + + public boolean labeled = true; + + public boolean explicit = true; + + public StatEdge(int type, Statement source, Statement destination, Statement closure) { + this(type, source, destination); + this.closure = closure; + } + + public StatEdge(int type, Statement source, Statement destination) { + this.type = type; + this.source = source; + this.destination = destination; + } + + public StatEdge(Statement source, Statement destination, List<String> exceptions) { + this(TYPE_EXCEPTION, source, destination); + if(exceptions != null) { + this.exceptions = new ArrayList<String>(exceptions); + } + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public Statement getSource() { + return source; + } + + public void setSource(Statement source) { + this.source = source; + } + + public Statement getDestination() { + return destination; + } + + public void setDestination(Statement destination) { + this.destination = destination; + } + + public List<String> getExceptions() { + return this.exceptions; + } + +// public void setException(String exception) { +// this.exception = exception; +// } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java new file mode 100644 index 0000000..a686f77 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java @@ -0,0 +1,204 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.util.ListStack; + +// -------------------------------------------------------------------- +// Algorithm +// -------------------------------------------------------------------- +// DFS(G) +// { +// make a new vertex x with edges x->v for all v +// initialize a counter N to zero +// initialize list L to empty +// build directed tree T, initially a single vertex {x} +// visit(x) +// } +// +// visit(p) +// { +// add p to L +// dfsnum(p) = N +// increment N +// low(p) = dfsnum(p) +// for each edge p->q +// if q is not already in T +// { +// add p->q to T +// visit(q) +// low(p) = min(low(p), low(q)) +// } else low(p) = min(low(p), dfsnum(q)) +// +// if low(p)=dfsnum(p) +// { +// output "component:" +// repeat +// remove last element v from L +// output v +// remove v from G +// until v=p +// } +// } +// -------------------------------------------------------------------- + +public class StrongConnectivityHelper { + + private ListStack<Statement> lstack; + + private int ncounter; + + private HashSet<Statement> tset; + private HashMap<Statement, Integer> dfsnummap; + private HashMap<Statement, Integer> lowmap; + + private List<List<Statement>> components; + + private HashSet<Statement> setProcessed; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public StrongConnectivityHelper() {} + + public StrongConnectivityHelper(Statement stat) { + findComponents(stat); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public List<List<Statement>> findComponents(Statement stat) { + + components = new ArrayList<List<Statement>>(); + setProcessed = new HashSet<Statement>(); + + visitTree(stat.getFirst()); + + for(Statement st: stat.getStats()) { + if(!setProcessed.contains(st) && st.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty()) { + visitTree(st); + } + } + + // should not find any more nodes! FIXME: ?? + for(Statement st: stat.getStats()) { + if(!setProcessed.contains(st)) { + visitTree(st); + } + } + + return components; + } + + public static boolean isExitComponent(List<Statement> lst) { + + HashSet<Statement> set = new HashSet<Statement>(); + for(Statement stat : lst) { + set.addAll(stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)); + } + set.removeAll(lst); + + return (set.size() == 0); + } + + public static List<Statement> getExitReps(List<List<Statement>> lst) { + + List<Statement> res = new ArrayList<Statement>(); + + for(List<Statement> comp : lst) { + if(isExitComponent(comp)) { + res.add(comp.get(0)); + } + } + + return res; + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void visitTree(Statement stat) { + lstack = new ListStack<Statement>(); + ncounter = 0; + tset = new HashSet<Statement>(); + dfsnummap = new HashMap<Statement, Integer>(); + lowmap = new HashMap<Statement, Integer>(); + + visit(stat); + + setProcessed.addAll(tset); + setProcessed.add(stat); + } + + private void visit(Statement stat) { + + lstack.push(stat); + dfsnummap.put(stat, ncounter); + lowmap.put(stat, ncounter); + ncounter++; + + List<Statement> lstSuccs = stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD); // TODO: set? + lstSuccs.removeAll(setProcessed); + + for(int i=0;i<lstSuccs.size();i++) { + Statement succ = lstSuccs.get(i); + int secvalue; + + if(tset.contains(succ)) { + secvalue = dfsnummap.get(succ); + } else { + tset.add(succ); + visit(succ); + secvalue = lowmap.get(succ); + } + lowmap.put(stat, Math.min(lowmap.get(stat), secvalue)); + } + + + if(lowmap.get(stat).intValue() == dfsnummap.get(stat).intValue()) { + List<Statement> lst = new ArrayList<Statement>(); + Statement v; + do { + v = lstack.pop(); + lst.add(v); + } while(v!=stat); + components.add(lst); + } + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<List<Statement>> getComponents() { + return components; + } + + public void setComponents(List<List<Statement>> components) { + this.components = components; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java new file mode 100644 index 0000000..e17a271 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java @@ -0,0 +1,127 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.decompose; + +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +public class DominatorEngine { + + private Statement statement; + + private VBStyleCollection<Integer, Integer> colOrderedIDoms = new VBStyleCollection<Integer, Integer>(); + + + public DominatorEngine(Statement statement) { + this.statement = statement; + } + + public void initialize() { + calcIDoms(); + } + + private void orderStatements() { + + for(Statement stat : statement.getReversePostOrderList()) { + colOrderedIDoms.addWithKey(null, stat.id); + } + + } + + private Integer getCommonIDom(Integer key1, Integer key2, VBStyleCollection<Integer, Integer> orderedIDoms) { + + if(key1 == null) { + return key2; + } else if(key2 == null) { + return key1; + } + + int index1 = orderedIDoms.getIndexByKey(key1); + int index2 = orderedIDoms.getIndexByKey(key2); + + while(index1 != index2) { + if(index1 > index2) { + key1 = orderedIDoms.getWithKey(key1); + index1 = orderedIDoms.getIndexByKey(key1); + } else { + key2 = orderedIDoms.getWithKey(key2); + index2 = orderedIDoms.getIndexByKey(key2); + } + } + + return key1; + } + + private void calcIDoms() { + + orderStatements(); + + colOrderedIDoms.putWithKey(statement.getFirst().id, statement.getFirst().id); + + // exclude first statement + List<Integer> lstIds = colOrderedIDoms.getLstKeys().subList(1, colOrderedIDoms.getLstKeys().size()); + + for(;;) { + + boolean changed = false; + + for(Integer id : lstIds) { + + Statement stat = statement.getStats().getWithKey(id); + Integer idom = null; + + for(StatEdge edge : stat.getAllPredecessorEdges()) { + if(colOrderedIDoms.getWithKey(edge.getSource().id) != null) { + idom = getCommonIDom(idom, edge.getSource().id, colOrderedIDoms); + } + } + + Integer oldidom = colOrderedIDoms.putWithKey(idom, id); + if(!idom.equals(oldidom)) { + changed = true; + } + } + + if(!changed) { + break; + } + } + + } + + public VBStyleCollection<Integer, Integer> getOrderedIDoms() { + return colOrderedIDoms; + } + + public boolean isDominator(Integer node, Integer dom) { + + while(!node.equals(dom)) { + + Integer idom = colOrderedIDoms.getWithKey(node); + + if(idom.equals(node)) { + return false; // root node + } else { + node = idom; + } + } + + return true; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java new file mode 100644 index 0000000..cf9e90c --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java @@ -0,0 +1,188 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.decompose; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +public class DominatorTreeExceptionFilter { + + private Statement statement; + + // idom, nodes + private Map<Integer, Set<Integer>> mapTreeBranches = new HashMap<Integer, Set<Integer>>(); + + // handler, range nodes + private Map<Integer, Set<Integer>> mapExceptionRanges = new HashMap<Integer, Set<Integer>>(); + + // handler, head dom + private Map<Integer, Integer> mapExceptionDoms = new HashMap<Integer, Integer>(); + + // statement, handler, exit nodes + private Map<Integer, Map<Integer, Integer>> mapExceptionRangeUniqueExit = new HashMap<Integer, Map<Integer, Integer>>(); + + private DominatorEngine domEngine; + + public DominatorTreeExceptionFilter(Statement statement) { + this.statement = statement; + } + + public void initialize() { + + domEngine = new DominatorEngine(statement); + domEngine.initialize(); + + buildDominatorTree(); + + buildExceptionRanges(); + + buildFilter(statement.getFirst().id); + + // free resources + mapTreeBranches.clear(); + mapExceptionRanges.clear(); + + } + + public boolean acceptStatementPair(Integer head, Integer exit) { + + Map<Integer, Integer> filter = mapExceptionRangeUniqueExit.get(head); + for(Entry<Integer, Integer> entry : filter.entrySet()) { + if(!head.equals(mapExceptionDoms.get(entry.getKey()))) { + Integer filterExit = entry.getValue(); + if(filterExit.intValue() == -1 || !filterExit.equals(exit)) { + return false; + } + } + } + + return true; + } + + private void buildDominatorTree() { + + VBStyleCollection<Integer, Integer> orderedIDoms = domEngine.getOrderedIDoms(); + + List<Integer> lstKeys = orderedIDoms.getLstKeys(); + for(int index = lstKeys.size()-1;index>=0;index--) { + Integer key = lstKeys.get(index); + Integer idom = orderedIDoms.get(index); + + Set<Integer> set = mapTreeBranches.get(idom); + if(set == null) { + mapTreeBranches.put(idom, set = new HashSet<Integer>()); + } + set.add(key); + } + + Integer firstid = statement.getFirst().id; + mapTreeBranches.get(firstid).remove(firstid); + } + + private void buildExceptionRanges() { + + for(Statement stat : statement.getStats()) { + List<Statement> lstPreds = stat.getNeighbours(StatEdge.TYPE_EXCEPTION, Statement.DIRECTION_BACKWARD); + if(!lstPreds.isEmpty()) { + + Set<Integer> set = new HashSet<Integer>(); + + for(Statement st : lstPreds) { + set.add(st.id); + } + + mapExceptionRanges.put(stat.id, set); + } + } + + mapExceptionDoms = buildExceptionDoms(statement.getFirst().id); + } + + private Map<Integer, Integer> buildExceptionDoms(Integer id) { + + Map<Integer, Integer> map = new HashMap<Integer, Integer>(); + + Set<Integer> children = mapTreeBranches.get(id); + if(children != null) { + for(Integer childid : children) { + Map<Integer, Integer> mapChild = buildExceptionDoms(childid); + for(Integer handler : mapChild.keySet()) { + map.put(handler, map.containsKey(handler)?id:mapChild.get(handler)); + } + } + } + + for(Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) { + if(entry.getValue().contains(id)) { + map.put(entry.getKey(), id); + } + } + + return map; + } + + + private void buildFilter(Integer id) { + + Map<Integer, Integer> map = new HashMap<Integer, Integer>(); + + Set<Integer> children = mapTreeBranches.get(id); + if(children != null) { + for(Integer childid : children) { + + buildFilter(childid); + + Map<Integer, Integer> mapChild = mapExceptionRangeUniqueExit.get(childid); + + for(Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) { + + Integer handler = entry.getKey(); + Set<Integer> range = entry.getValue(); + + if(range.contains(id)) { + + Integer exit = null; + + if(!range.contains(childid)) { + exit = childid; + } else { + // exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug? + exit = map.containsKey(handler)?new Integer(-1):mapChild.get(handler); + } + + if(exit != null) { + map.put(handler, exit); + } + } + } + } + } + + mapExceptionRangeUniqueExit.put(id, map); + } + + public DominatorEngine getDomEngine() { + return domEngine; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java new file mode 100644 index 0000000..e3093b5 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java @@ -0,0 +1,362 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.decompose; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.util.FastFixedSetFactory; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet; + +public class FastExtendedPostdominanceHelper { + + private List<Statement> lstReversePostOrderList; + + private HashMap<Integer, FastFixedSet<Integer>> mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>(); + + private HashMap<Integer, FastFixedSet<Integer>> mapExtPostdominators = new HashMap<Integer, FastFixedSet<Integer>>(); + + private Statement statement; + + private FastFixedSetFactory<Integer> factory; + + public HashMap<Integer, Set<Integer>> getExtendedPostdominators(Statement statement) { + + this.statement = statement; + + HashSet<Integer> set = new HashSet<Integer>(); + for(Statement st : statement.getStats()) { + set.add(st.id); + } + this.factory = new FastFixedSetFactory<Integer>(set); + + lstReversePostOrderList = statement.getReversePostOrderList(); + +// try { +// DotExporter.toDotFile(statement, new File("c:\\Temp\\stat1.dot")); +// } catch (Exception ex) { +// ex.printStackTrace(); +// } + + calcDefaultReachableSets(); + + removeErroneousNodes(); + + DominatorTreeExceptionFilter filter = new DominatorTreeExceptionFilter(statement); + filter.initialize(); + + filterOnExceptionRanges(filter); + + filterOnDominance(filter); + + HashMap<Integer, Set<Integer>> res = new HashMap<Integer, Set<Integer>>(); + for(Entry<Integer, FastFixedSet<Integer>> entry : mapExtPostdominators.entrySet()) { + res.put(entry.getKey(), entry.getValue().toPlainSet()); + } + + return res; + } + + + private void filterOnDominance(DominatorTreeExceptionFilter filter) { + + DominatorEngine engine = filter.getDomEngine(); + + for(Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) { + + FastFixedSet<Integer> setPostdoms = mapExtPostdominators.get(head); + + LinkedList<Statement> stack = new LinkedList<Statement>(); + LinkedList<FastFixedSet<Integer>> stackPath = new LinkedList<FastFixedSet<Integer>>(); + + stack.add(statement.getStats().getWithKey(head)); + stackPath.add(factory.spawnEmptySet()); + + Set<Statement> setVisited = new HashSet<Statement>(); + + while(!stack.isEmpty()) { + + Statement stat = stack.removeFirst(); + FastFixedSet<Integer> path = stackPath.removeFirst(); + + if(setPostdoms.contains(stat.id)) { + path.add(stat.id); + } + + if(path.contains(setPostdoms)) { + continue; + } + + setVisited.add(stat); + + int domflag = 0; + + for(Iterator<Integer> it = setPostdoms.iterator();it.hasNext();) { + Integer post = it.next(); + + if(!path.contains(post)) { + if(domflag == 0) { + domflag = engine.isDominator(stat.id, head)?2:1; + } + + if(domflag == 1) { // not a dominator + it.remove(); + } + } + } + + for(StatEdge edge : stat.getSuccessorEdges(StatEdge.TYPE_REGULAR)) { + if(!setVisited.contains(edge.getDestination())) { + stack.add(edge.getDestination()); + stackPath.add(path.getCopy()); + } + } + } + + if(setPostdoms.isEmpty()) { + mapExtPostdominators.remove(head); + } + + } + } + + + private void filterOnExceptionRanges(DominatorTreeExceptionFilter filter) { + + + for(Integer head : new HashSet<Integer>(mapExtPostdominators.keySet())) { + + FastFixedSet<Integer> set = mapExtPostdominators.get(head); + for(Iterator<Integer> it = set.iterator();it.hasNext();) { + if(!filter.acceptStatementPair(head, it.next())) { + it.remove(); + } + } + if(set.isEmpty()) { + mapExtPostdominators.remove(head); + } + } + + } + + + private void removeErroneousNodes() { + + mapSupportPoints = new HashMap<Integer, FastFixedSet<Integer>>(); + + calcReachabilitySuppPoints(StatEdge.TYPE_REGULAR); + + iterateReachability(new IReachabilityAction() { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { + + Integer nodeid = node.id; + + FastFixedSet<Integer> setReachability = mapSets.get(nodeid); + List<FastFixedSet<Integer>> lstPredSets = new ArrayList<FastFixedSet<Integer>>(); + + for(StatEdge prededge : node.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { + FastFixedSet<Integer> setPred = mapSets.get(prededge.getSource().id); + if(setPred == null) { + setPred = mapSupportPoints.get(prededge.getSource().id); + } + + // setPred cannot be empty as it is a reachability set + lstPredSets.add(setPred); + } + + for(Integer id : setReachability.toPlainSet()) { + + FastFixedSet<Integer> setReachabilityCopy = setReachability.getCopy(); + + FastFixedSet<Integer> setIntersection = factory.spawnEmptySet(); + boolean isIntersectionInitialized = false; + + for(FastFixedSet<Integer> predset : lstPredSets) { + if(predset.contains(id)) { + if(!isIntersectionInitialized) { + setIntersection.union(predset); + isIntersectionInitialized = true; + } else { + setIntersection.intersection(predset); + } + } + } + + if(nodeid != id.intValue()) { + setIntersection.add(nodeid); + } else { + setIntersection.remove(nodeid); + } + + setReachabilityCopy.complement(setIntersection); + + mapExtPostdominators.get(id).complement(setReachabilityCopy); + } + + return false; + } + }, StatEdge.TYPE_REGULAR); + + // exception handlers cannot be postdominator nodes + // TODO: replace with a standard set? + FastFixedSet<Integer> setHandlers = factory.spawnEmptySet(); + boolean handlerfound = false; + + for(Statement stat : statement.getStats()) { + if(stat.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL).isEmpty() && + !stat.getPredecessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { // exception handler + setHandlers.add(stat.id); + handlerfound = true; + } + } + + if(handlerfound) { + for(FastFixedSet<Integer> set : mapExtPostdominators.values()) { + set.complement(setHandlers); + } + } + } + + + private void calcDefaultReachableSets() { + + int edgetype = StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION; + + calcReachabilitySuppPoints(edgetype); + + for(Statement stat : statement.getStats()) { + mapExtPostdominators.put(stat.id, factory.spawnEmptySet()); + } + + iterateReachability(new IReachabilityAction() { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { + + Integer nodeid = node.id; + FastFixedSet<Integer> setReachability = mapSets.get(nodeid); + + for(Integer id : setReachability.toPlainSet()) { + mapExtPostdominators.get(id).add(nodeid); + } + + return false; + } + }, edgetype); + + } + + + private void calcReachabilitySuppPoints(final int edgetype) { + + iterateReachability(new IReachabilityAction() { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets) { + + // consider to be a support point + for(StatEdge sucedge : node.getAllSuccessorEdges()) { + if((sucedge.getType() & edgetype) != 0) { + if(mapSets.containsKey(sucedge.getDestination().id)) { + FastFixedSet<Integer> setReachability = mapSets.get(node.id); + + if(!InterpreterUtil.equalObjects(setReachability, mapSupportPoints.get(node.id))) { + mapSupportPoints.put(node.id, setReachability); + return true; + } + } + } + } + + return false; + } + }, edgetype); + + } + + private void iterateReachability(IReachabilityAction action, int edgetype) { + + for(;;) { + + boolean iterate = false; + + HashMap<Integer, FastFixedSet<Integer>> mapSets = new HashMap<Integer, FastFixedSet<Integer>>(); + + for(Statement stat : lstReversePostOrderList) { + + FastFixedSet<Integer> set = factory.spawnEmptySet(); + set.add(stat.id); + + for(StatEdge prededge : stat.getAllPredecessorEdges()) { + if((prededge.getType() & edgetype) != 0) { + Statement pred = prededge.getSource(); + + FastFixedSet<Integer> setPred = mapSets.get(pred.id); + if(setPred == null) { + setPred = mapSupportPoints.get(pred.id); + } + + if(setPred != null) { + set.union(setPred); + } + } + } + + mapSets.put(stat.id, set); + + if(action != null) { + iterate |= action.action(stat, mapSets); + } + + // remove reachability information of fully processed nodes (saves memory) + for(StatEdge prededge : stat.getAllPredecessorEdges()) { + if((prededge.getType() & edgetype) != 0) { + Statement pred = prededge.getSource(); + + if(mapSets.containsKey(pred.id)) { + boolean remstat = true; + for(StatEdge sucedge : pred.getAllSuccessorEdges()) { + if((sucedge.getType() & edgetype) != 0) { + if(!mapSets.containsKey(sucedge.getDestination().id)) { + remstat = false; + break; + } + } + } + + if(remstat) { + mapSets.put(pred.id, null); + } + } + } + } + } + + if(!iterate) { + break; + } + } + } + + + private interface IReachabilityAction { + public boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets); + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java new file mode 100644 index 0000000..ed35202 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java @@ -0,0 +1,150 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.decompose; + +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +public class GenericDominatorEngine { + + private IGraph graph; + + private VBStyleCollection<IGraphNode, IGraphNode> colOrderedIDoms = new VBStyleCollection<IGraphNode, IGraphNode>(); + + private Set<? extends IGraphNode> setRoots; + + public GenericDominatorEngine(IGraph graph) { + this.graph = graph; + } + + public void initialize() { + calcIDoms(); + } + + private void orderNodes() { + + setRoots = graph.getRoots(); + + for(IGraphNode node : graph.getReversePostOrderList()) { + colOrderedIDoms.addWithKey(null, node); + } + + } + + private IGraphNode getCommonIDom(IGraphNode node1, IGraphNode node2, VBStyleCollection<IGraphNode, IGraphNode> orderedIDoms) { + + IGraphNode nodeOld; + + if(node1 == null) { + return node2; + } else if(node2 == null) { + return node1; + } + + int index1 = orderedIDoms.getIndexByKey(node1); + int index2 = orderedIDoms.getIndexByKey(node2); + + while(index1 != index2) { + if(index1 > index2) { + nodeOld = node1; + node1 = orderedIDoms.getWithKey(node1); + + if(nodeOld == node1) { // no idom - root or merging point + return null; + } + + index1 = orderedIDoms.getIndexByKey(node1); + } else { + nodeOld = node2; + node2 = orderedIDoms.getWithKey(node2); + + if(nodeOld == node2) { // no idom - root or merging point + return null; + } + + index2 = orderedIDoms.getIndexByKey(node2); + } + } + + return node1; + } + + private void calcIDoms() { + + orderNodes(); + + List<IGraphNode> lstNodes = colOrderedIDoms.getLstKeys(); + + for(;;) { + + boolean changed = false; + + for(IGraphNode node : lstNodes) { + + IGraphNode idom = null; + + if(!setRoots.contains(node)) { + for(IGraphNode pred : node.getPredecessors()) { + if(colOrderedIDoms.getWithKey(pred) != null) { + idom = getCommonIDom(idom, pred, colOrderedIDoms); + if(idom == null) { + break; // no idom found: merging point of two trees + } + } + } + } + + if(idom == null) { + idom = node; + } + + IGraphNode oldidom = colOrderedIDoms.putWithKey(idom, node); + if(!idom.equals(oldidom)) { // oldidom is null iff the node is touched for the first time + changed = true; + } + } + + if(!changed) { + break; + } + } + + } + + public VBStyleCollection<IGraphNode, IGraphNode> getOrderedIDoms() { + return colOrderedIDoms; + } + + public boolean isDominator(IGraphNode node, IGraphNode dom) { + + while(!node.equals(dom)) { + + IGraphNode idom = colOrderedIDoms.getWithKey(node); + + if(idom == node) { + return false; // root node or merging point + } else if(idom == null) { + throw new RuntimeException("Inconsistent idom sequence discovered!"); + } else { + node = idom; + } + } + + return true; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java new file mode 100644 index 0000000..8b72a4f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java @@ -0,0 +1,26 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.decompose; + +import java.util.List; +import java.util.Set; + +public interface IGraph { + + public List<? extends IGraphNode> getReversePostOrderList(); + + public Set<? extends IGraphNode> getRoots(); + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java new file mode 100644 index 0000000..d2d1e0b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java @@ -0,0 +1,23 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.decompose; + +import java.util.List; + +public interface IGraphNode { + + public List<? extends IGraphNode> getPredecessors(); + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java new file mode 100644 index 0000000..d0f7243 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java @@ -0,0 +1,324 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.deobfuscator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; +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.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.util.InterpreterUtil; + +public class ExceptionDeobfuscator { + + public static void restorePopRanges(ControlFlowGraph graph) { + + List<Object[]> lstRanges = new ArrayList<Object[]>(); + + // aggregate ranges + for(ExceptionRangeCFG range : graph.getExceptions()) { + boolean found = false; + for(Object[] arr : lstRanges) { + if(arr[0] == range.getHandler() && InterpreterUtil.equalObjects(range.getUniqueExceptionsString(),arr[1])) { + ((HashSet<BasicBlock>)arr[2]).addAll(range.getProtectedRange()); + found = true; + break; + } + } + + if(!found) { + // doesn't matter, which range chosen + lstRanges.add(new Object[] {range.getHandler(), range.getUniqueExceptionsString(), new HashSet<BasicBlock>(range.getProtectedRange()), range}); + } + } + + // process aggregated ranges + for(Object[] range : lstRanges) { + + if(range[1] != null) { + + BasicBlock handler = (BasicBlock)range[0]; + InstructionSequence seq = handler.getSeq(); + + Instruction firstinstr = null; + if(seq.length() > 0) { + firstinstr = seq.getInstr(0); + + if(firstinstr.opcode == CodeConstants.opc_pop || + firstinstr.opcode == CodeConstants.opc_astore) { + HashSet<BasicBlock> setrange = new HashSet<BasicBlock>((HashSet<BasicBlock>)range[2]); + + for(Object[] range_super : lstRanges) { // finally or strict superset + + if(range != range_super) { + + HashSet<BasicBlock> setrange_super = new HashSet<BasicBlock>((HashSet<BasicBlock>)range_super[2]); + + if(!setrange.contains(range_super[0]) && !setrange_super.contains(handler) + && (range_super[1] == null || setrange_super.containsAll(setrange))) { + + if(range_super[1] == null) { + setrange_super.retainAll(setrange); + } else { + setrange_super.removeAll(setrange); + } + + if(!setrange_super.isEmpty()) { + + BasicBlock newblock = handler; + + // split the handler + if(seq.length() > 1) { + newblock = new BasicBlock(++graph.last_id); + InstructionSequence newseq = new SimpleInstructionSequence(); + newseq.addInstruction(firstinstr.clone() , -1); + + newblock.setSeq(newseq); + graph.getBlocks().addWithKey(newblock, newblock.id); + + + List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); + lstTemp.addAll(handler.getPreds()); + lstTemp.addAll(handler.getPredExceptions()); + + // replace predecessors + for(BasicBlock pred: lstTemp) { + pred.replaceSuccessor(handler, newblock); + } + + // replace handler + for(ExceptionRangeCFG range_ext: graph.getExceptions()) { + if(range_ext.getHandler() == handler) { + range_ext.setHandler(newblock); + } else if(range_ext.getProtectedRange().contains(handler)) { + newblock.addSuccessorException(range_ext.getHandler()); + range_ext.getProtectedRange().add(newblock); + } + } + + newblock.addSuccessor(handler); + if(graph.getFirst() == handler) { + graph.setFirst(newblock); + } + + // remove the first pop in the handler + seq.removeInstruction(0); + } + + + newblock.addSuccessorException((BasicBlock)range_super[0]); + ((ExceptionRangeCFG)range_super[3]).getProtectedRange().add(newblock); + + handler = ((ExceptionRangeCFG)range[3]).getHandler(); + seq = handler.getSeq(); + } + } + } + } + } + } + } + } + } + + public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) { + + HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); + + for(ExceptionRangeCFG range : graph.getExceptions()) { + BasicBlock handler = range.getHandler(); + + if(setVisited.contains(handler)) { + continue; + } + setVisited.add(handler); + + BasicBlock emptyblock = new BasicBlock(++graph.last_id); + graph.getBlocks().addWithKey(emptyblock, emptyblock.id); + + List<BasicBlock> lstTemp = new ArrayList<BasicBlock>(); + // only exception predecessors considered + lstTemp.addAll(handler.getPredExceptions()); + + // replace predecessors + for(BasicBlock pred: lstTemp) { + pred.replaceSuccessor(handler, emptyblock); + } + + // replace handler + for(ExceptionRangeCFG range_ext: graph.getExceptions()) { + if(range_ext.getHandler() == handler) { + range_ext.setHandler(emptyblock); + } else if(range_ext.getProtectedRange().contains(handler)) { + emptyblock.addSuccessorException(range_ext.getHandler()); + range_ext.getProtectedRange().add(emptyblock); + } + } + + emptyblock.addSuccessor(handler); + if(graph.getFirst() == handler) { + graph.setFirst(emptyblock); + } + } + } + + public static void removeEmptyRanges(ControlFlowGraph graph) { + + List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); + for(int i=lstRanges.size()-1;i>=0;i--) { + ExceptionRangeCFG range = lstRanges.get(i); + + boolean isEmpty = true; + for(BasicBlock block : range.getProtectedRange()) { + if(!block.getSeq().isEmpty()) { + isEmpty = false; + break; + } + } + + if(isEmpty) { + for(BasicBlock block : range.getProtectedRange()) { + block.removeSuccessorException(range.getHandler()); + } + + lstRanges.remove(i); + } + } + + } + + public static void removeCircularRanges(final ControlFlowGraph graph) { + + GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() { + public List<? extends IGraphNode> getReversePostOrderList() { + return graph.getReversePostOrder(); + } + + public Set<? extends IGraphNode> getRoots() { + return new HashSet<IGraphNode>(Arrays.asList(new IGraphNode[]{graph.getFirst()})); + } + }); + + engine.initialize(); + + List<ExceptionRangeCFG> lstRanges = graph.getExceptions(); + for(int i=lstRanges.size()-1;i>=0;i--) { + ExceptionRangeCFG range = lstRanges.get(i); + + BasicBlock handler = range.getHandler(); + List<BasicBlock> rangeList = range.getProtectedRange(); + + if(rangeList.contains(handler)) { // TODO: better removing strategy + + List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range, engine); + + if(lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) { + for(BasicBlock block : lstRemBlocks) { + block.removeSuccessorException(handler); + rangeList.remove(block); + } + } + + if(rangeList.isEmpty()) { + lstRanges.remove(i); + } + } + } + + } + + private static List<BasicBlock> getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) { + + List<BasicBlock> lstRes = new ArrayList<BasicBlock>(); + + LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>(); + HashSet<BasicBlock> setVisited = new HashSet<BasicBlock>(); + + BasicBlock handler = range.getHandler(); + stack.addFirst(handler); + + while(!stack.isEmpty()) { + BasicBlock block = stack.removeFirst(); + + setVisited.add(block); + + if(range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) { + lstRes.add(block); + + List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs()); + lstSuccs.addAll(block.getSuccExceptions()); + + for(BasicBlock succ : lstSuccs) { + if(!setVisited.contains(succ)) { + stack.add(succ); + } + } + } + } + + return lstRes; + } + + + public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) { + + BasicBlock first = graph.getFirst(); + + HashMap<BasicBlock, HashSet<BasicBlock>> mapRanges = new HashMap<BasicBlock, HashSet<BasicBlock>>(); + for(ExceptionRangeCFG range : graph.getExceptions()) { + HashSet<BasicBlock> set = mapRanges.get(range.getHandler()); + if(set == null) { + mapRanges.put(range.getHandler(), set = new HashSet<BasicBlock>()); + } + set.addAll(range.getProtectedRange()); + + } + + for(Entry<BasicBlock, HashSet<BasicBlock>> ent : mapRanges.entrySet()) { + HashSet<BasicBlock> setEntries = new HashSet<BasicBlock>(); + + for(BasicBlock block : ent.getValue()) { + HashSet<BasicBlock> setTemp = new HashSet<BasicBlock>(block.getPreds()); + setTemp.removeAll(ent.getValue()); + + if(!setTemp.isEmpty()) { + setEntries.add(block); + } + } + + if(!setEntries.isEmpty()) { + if(setEntries.size() > 1 /*|| ent.getValue().contains(first)*/) { + return true; + } + } + } + + return false; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java new file mode 100644 index 0000000..250a25e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java @@ -0,0 +1,238 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.deobfuscator; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class IrreducibleCFGDeobfuscator { + + + public static boolean isStatementIrreducible(Statement statement) { + + class Node { + public Integer id; + public Set<Node> preds = new HashSet<Node>(); + public Set<Node> succs = new HashSet<Node>(); + + public Node(Integer id) {this.id = id;} + } + + HashMap<Integer, Node> mapNodes = new HashMap<Integer, Node>(); + + // checking exceptions and creating nodes + for(Statement stat : statement.getStats()) { + if(!stat.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()) { + return false; + } + + mapNodes.put(stat.id, new Node(stat.id)); + } + + // connecting nodes + for(Statement stat : statement.getStats()) { + Node node = mapNodes.get(stat.id); + + for(Statement succ : stat.getNeighbours(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD)) { + Node nodeSucc = mapNodes.get(succ.id); + + node.succs.add(nodeSucc); + nodeSucc.preds.add(node); + } + } + + // transforming and reducing the graph + for(;;) { + int ttype = 0; + Node node = null; + + for(Node nd : mapNodes.values()) { + if(nd.succs.contains(nd)) { // T1 + ttype = 1; + } else if(nd.preds.size() == 1) { // T2 + ttype = 2; + } + + if(ttype != 0) { + node = nd; + break; + } + } + + if(node != null) { + if(ttype == 1) { + node.succs.remove(node); + node.preds.remove(node); + } else { + Node pred = node.preds.iterator().next(); + + pred.succs.addAll(node.succs); + pred.succs.remove(node); + + for(Node succ : node.succs) { + succ.preds.remove(node); + succ.preds.add(pred); + } + + mapNodes.remove(node.id); + } + } else { // no transformation applicable + return mapNodes.size() > 1; // reducible iff one node remains + } + } + + } + + + + + private static Statement getCandidateForSplitting(Statement statement) { + + Statement candidateForSplitting = null; + int sizeCandidateForSplitting = Integer.MAX_VALUE; + int succsCandidateForSplitting = Integer.MAX_VALUE; + + for(Statement stat : statement.getStats()) { + + Set<Statement> setPreds = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_BACKWARD); + + if(setPreds.size() > 1) { + int succCount = stat.getNeighboursSet(StatEdge.TYPE_REGULAR, Statement.DIRECTION_FORWARD).size(); + if(succCount <= succsCandidateForSplitting) { + int size = getStatementSize(stat)*(setPreds.size()-1); + + if(succCount < succsCandidateForSplitting || + size < sizeCandidateForSplitting) { + candidateForSplitting = stat; + sizeCandidateForSplitting = size; + succsCandidateForSplitting = succCount; + } + } + } + } + + return candidateForSplitting; + } + + public static boolean splitIrreducibleNode(Statement statement) { + + Statement splitnode = getCandidateForSplitting(statement); + if(splitnode == null) { + return false; + } + + StatEdge enteredge = splitnode.getPredecessorEdges(StatEdge.TYPE_REGULAR).iterator().next(); + + // copy the smallest statement + Statement splitcopy = copyStatement(splitnode, null, new HashMap<Statement, Statement>()); + initCopiedStatement(splitcopy); + + // insert the copy + splitcopy.setParent(statement); + statement.getStats().addWithKey(splitcopy, splitcopy.id); + + // switch input edges + for(StatEdge prededge : splitnode.getPredecessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + if(prededge.getSource() == enteredge.getSource() || + prededge.closure == enteredge.getSource()) { + splitnode.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(Statement.DIRECTION_FORWARD, prededge, splitcopy); + splitcopy.addPredecessor(prededge); + } + } + + // connect successors + for(StatEdge succ : splitnode.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + splitcopy.addSuccessor(new StatEdge(succ.getType(), splitcopy, succ.getDestination(), succ.closure)); + } + + return true; + } + + private static int getStatementSize(Statement statement) { + + int res = 0; + + if(statement.type == Statement.TYPE_BASICBLOCK) { + res = ((BasicBlockStatement)statement).getBlock().getSeq().length(); + } else { + for(Statement stat: statement.getStats()) { + res+=getStatementSize(stat); + } + } + + return res; + } + + private static Statement copyStatement(Statement from, Statement to, HashMap<Statement, Statement> mapAltToCopies) { + + if(to == null) { + // first outer invocation + to = from.getSimpleCopy(); + mapAltToCopies.put(from, to); + } + + // copy statements + for(Statement st : from.getStats()) { + Statement stcopy = st.getSimpleCopy(); + + to.getStats().addWithKey(stcopy, stcopy.id); + mapAltToCopies.put(st, stcopy); + } + + // copy edges + for(int i=0;i<from.getStats().size();i++) { + Statement stold = from.getStats().get(i); + Statement stnew = to.getStats().get(i); + + for(StatEdge edgeold : stold.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) { + // type cannot be TYPE_EXCEPTION (checked in isIrreducibleTriangle) + StatEdge edgenew = new StatEdge(edgeold.getType(), stnew, + mapAltToCopies.containsKey(edgeold.getDestination())?mapAltToCopies.get(edgeold.getDestination()):edgeold.getDestination(), + mapAltToCopies.containsKey(edgeold.closure)?mapAltToCopies.get(edgeold.closure):edgeold.closure); + + stnew.addSuccessor(edgenew); + } + } + + // recurse statements + for(int i=0;i<from.getStats().size();i++) { + Statement stold = from.getStats().get(i); + Statement stnew = to.getStats().get(i); + + copyStatement(stold, stnew, mapAltToCopies); + } + + return to; + } + + private static void initCopiedStatement(Statement statement) { + + statement.initSimpleCopy(); + statement.setCopied(true); + + for(Statement st : statement.getStats()) { + st.setParent(statement); + initCopiedStatement(st); + } + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java new file mode 100644 index 0000000..2945913 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java @@ -0,0 +1,111 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.List; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class AnnotationExprent extends Exprent { + + public static final int ANNOTATION_NORMAL = 1; + public static final int ANNOTATION_MARKER = 2; + public static final int ANNOTATION_SINGLE_ELEMENT = 3; + + + private String classname; + + private List<String> parnames; + + private List<Exprent> parvalues; + + { + this.type = EXPRENT_ANNOTATION; + } + + public AnnotationExprent(String classname, List<String> parnames, List<Exprent> parvalues) { + this.classname = classname; + this.parnames = parnames; + this.parvalues = parvalues; + } + + public String toJava(int indent) { + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuilder buffer = new StringBuilder(); + String indstr = InterpreterUtil.getIndentString(indent); + + buffer.append(indstr); + buffer.append("@"); + buffer.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + + if(!parnames.isEmpty()) { + buffer.append("("); + if(parnames.size() == 1 && "value".equals(parnames.get(0))) { + buffer.append(parvalues.get(0).toJava(indent+1)); + } else { + String indstr1 = InterpreterUtil.getIndentString(indent+1); + + for(int i=0;i<parnames.size();i++) { + buffer.append(new_line_separator+indstr1); + buffer.append(parnames.get(i)); + buffer.append(" = "); + buffer.append(parvalues.get(i).toJava(indent+2)); + + if(i<parnames.size()-1) { + buffer.append(","); + } + } + buffer.append(new_line_separator+indstr); + } + + buffer.append(")"); + } + + return buffer.toString(); + } + + public int getAnnotationType() { + + if(parnames.isEmpty()) { + return ANNOTATION_MARKER; + } else { + if(parnames.size() == 1 && "value".equals(parnames.get(0))) { + return ANNOTATION_SINGLE_ELEMENT; + } else { + return ANNOTATION_NORMAL; + } + } + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof AnnotationExprent)) return false; + + AnnotationExprent ann = (AnnotationExprent)o; + return classname.equals(ann.classname) && + InterpreterUtil.equalLists(parnames, ann.parnames) && + InterpreterUtil.equalLists(parvalues, ann.parvalues); + } + + public String getClassname() { + return classname; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java new file mode 100644 index 0000000..2f262bd --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java @@ -0,0 +1,125 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class ArrayExprent extends Exprent { + + private Exprent array; + + private Exprent index; + + private VarType hardtype; + + { + this.type = EXPRENT_ARRAY; + } + + public ArrayExprent(Exprent array, Exprent index, VarType hardtype) { + this.array = array; + this.index = index; + this.hardtype = hardtype; + } + + public Exprent copy() { + return new ArrayExprent(array.copy(), index.copy(), hardtype); + } + + public VarType getExprType() { + VarType exprType = array.getExprType().copy(); + if(exprType.equals(VarType.VARTYPE_NULL)) { + exprType = hardtype.copy(); + } else { + exprType.decArrayDim(); + } + + return exprType; + } + + public int getExprentUse() { + return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES; + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(index, VarType.VARTYPE_INT); + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(array); + lst.add(index); + return lst; + } + + + public String toJava(int indent) { + String res = array.toJava(indent); + + if(array.getPrecedence() > getPrecedence()) { // array precedence equals 0 + res = "("+res+")"; + } + + VarType arrtype = array.getExprType(); + if(arrtype.arraydim == 0) { + VarType objarr = VarType.VARTYPE_OBJECT.copy(); + objarr.arraydim = 1; // type family does not change + + res = "(("+ExprProcessor.getCastTypeName(objarr)+")"+res+")"; + } + + return res+"["+index.toJava(indent)+"]"; + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof ArrayExprent)) return false; + + ArrayExprent arr = (ArrayExprent)o; + return InterpreterUtil.equalObjects(array, arr.getArray()) && + InterpreterUtil.equalObjects(index, arr.getIndex()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == array) { + array = newexpr; + } + + if(oldexpr == index) { + index = newexpr; + } + } + + public Exprent getArray() { + return array; + } + + public Exprent getIndex() { + return index; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java new file mode 100644 index 0000000..03ead80 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java @@ -0,0 +1,51 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.List; + +public class AssertExprent extends Exprent { + + private List<Exprent> parameters; + + { + this.type = EXPRENT_ASSERT; + } + + public AssertExprent(List<Exprent> parameters) { + this.parameters = parameters; + } + + public String toJava(int indent) { + + StringBuilder buffer = new StringBuilder(); + + buffer.append("assert "); + + if(parameters.get(0) == null) { + buffer.append("false"); + } else { + buffer.append(parameters.get(0).toJava(indent)); + } + if(parameters.size() > 1) { + buffer.append(" : "); + buffer.append(parameters.get(1).toJava(indent)); + } + + return buffer.toString(); + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java new file mode 100644 index 0000000..9c233d4 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java @@ -0,0 +1,195 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructField; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class AssignmentExprent extends Exprent { + + public static final int CONDITION_NONE = -1; + + private static final String[] funceq = new String[] { + " += ", // FUNCTION_ADD + " -= ", // FUNCTION_SUB + " *= ", // FUNCTION_MUL + " /= ", // FUNCTION_DIV + " &= ", // FUNCTION_AND + " |= ", // FUNCTION_OR + " ^= ", // FUNCTION_XOR + " %= ", // FUNCTION_REM + " <<= ", // FUNCTION_SHL + " >>= ", // FUNCTION_SHR + " >>>= " // FUNCTION_USHR + }; + + + private Exprent left; + + private Exprent right; + + private int condtype = CONDITION_NONE; + + { + this.type = EXPRENT_ASSIGNMENT; + } + + + public AssignmentExprent(Exprent left, Exprent right) { + this.left = left; + this.right = right; + } + + + public VarType getExprType() { + return left.getExprType(); + } + + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + VarType typeleft = left.getExprType(); + VarType typeright = right.getExprType(); + + if(typeleft.type_family > typeright.type_family) { + result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeleft.type_family)); + } else if(typeleft.type_family < typeright.type_family) { + result.addMinTypeExprent(left, typeright); + } else { + result.addMinTypeExprent(left, VarType.getCommonSupertype(typeleft, typeright)); + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(left); + lst.add(right); + return lst; + } + + public Exprent copy() { + return new AssignmentExprent(left.copy(), right.copy()); + } + + public int getPrecedence() { + return 13; + } + + public String toJava(int indent) { + + VarType leftType = left.getExprType(); + VarType rightType = right.getExprType(); + + String res = right.toJava(indent); + + if(condtype == CONDITION_NONE && !leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.type != CodeConstants.TYPE_OBJECT)) { + if(right.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { + res = "("+res+")"; + } + + res = "("+ExprProcessor.getCastTypeName(leftType)+")"+res; + } + + StringBuilder buffer = new StringBuilder(); + + boolean finstat_init = false; + if(left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without "this" in front of it + FieldExprent field = (FieldExprent)left; + if(field.isStatic()) { + ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)); + if(node != null) { + StructClass cl = node.classStruct; + StructField fd = cl.getField(field.getName(), field.getDescriptor().descriptorString); + + if(fd != null && (fd.access_flags & CodeConstants.ACC_FINAL) != 0) { + finstat_init = true; + } + } + } + } + + if(finstat_init) { + buffer.append(((FieldExprent)left).getName()); + } else { + buffer.append(left.toJava(indent)); + } + + buffer.append(condtype == CONDITION_NONE ? " = " : funceq[condtype]).append(res); + + return buffer.toString(); + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof AssignmentExprent)) return false; + + AssignmentExprent as = (AssignmentExprent)o; + return InterpreterUtil.equalObjects(left, as.getLeft()) && + InterpreterUtil.equalObjects(right, as.getRight()) && + condtype == as.getCondtype(); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == left) { + left = newexpr; + } + + if(oldexpr == right) { + right = newexpr; + } + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Exprent getLeft() { + return left; + } + + public void setLeft(Exprent left) { + this.left = left; + } + + public Exprent getRight() { + return right; + } + + public void setRight(Exprent right) { + this.right = right; + } + + public int getCondtype() { + return condtype; + } + + public void setCondtype(int condtype) { + this.condtype = condtype; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java new file mode 100644 index 0000000..10fa1d4 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -0,0 +1,369 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class ConstExprent extends Exprent { + private static final HashMap<Integer, String> escapes = new HashMap<Integer, String>(); + + static { + escapes.put(new Integer(0x8), "\\b"); /* \u0008: backspace BS */ + escapes.put(new Integer(0x9), "\\t"); /* \u0009: horizontal tab HT */ + escapes.put(new Integer(0xA), "\\n"); /* \u000a: linefeed LF */ + escapes.put(new Integer(0xC), "\\f"); /* \u000c: form feed FF */ + escapes.put(new Integer(0xD), "\\r"); /* \u000d: carriage return CR */ + escapes.put(new Integer(0x22), "\\\""); /* \u0022: double quote " */ + escapes.put(new Integer(0x27), "\\\'"); /* \u0027: single quote ' */ + escapes.put(new Integer(0x5C), "\\\\"); /* \u005c: backslash \ */ + } + + + private VarType consttype; + + private Object value; + + private boolean boolPermitted; + + { + this.type = EXPRENT_CONST; + } + + public ConstExprent(int val, boolean boolPermitted) { + + this.boolPermitted = boolPermitted; + if(boolPermitted) { + consttype = VarType.VARTYPE_BOOLEAN; + if(val != 0 && val != 1) { + consttype = consttype.copy(); + consttype.convinfo |= VarType.FALSEBOOLEAN; + } + } else { + if(0 <= val && val <= 127) { + consttype = VarType.VARTYPE_BYTECHAR; + } else if(-128 <= val && val <= 127) { + consttype = VarType.VARTYPE_BYTE; + } else if(0 <= val && val <= 32767) { + consttype = VarType.VARTYPE_SHORTCHAR; + } else if(-32768 <= val && val <= 32767) { + consttype = VarType.VARTYPE_SHORT; + } else if(0 <= val && val <= 0xFFFF) { + consttype = VarType.VARTYPE_CHAR; + } else { + consttype = VarType.VARTYPE_INT; + } + } + value = new Integer(val); + } + + public ConstExprent(VarType consttype, Object value) { + this.consttype = consttype; + this.value = value; + } + + public Exprent copy() { + return new ConstExprent(consttype, value); + } + + public VarType getExprType() { + return consttype; + } + + public int getExprentUse() { + return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; + } + + public List<Exprent> getAllExprents() { + return new ArrayList<Exprent>(); + } + + public String toJava(int indent) { + boolean literal = DecompilerContext.getOption(IFernflowerPreferences.LITERALS_AS_IS); + boolean ascii = DecompilerContext.getOption(IFernflowerPreferences.ASCII_STRING_CHARACTERS); + + if(consttype.type != CodeConstants.TYPE_NULL && value == null) { + return ExprProcessor.getCastTypeName(consttype); + } else { + switch(consttype.type) { + case CodeConstants.TYPE_BOOLEAN: + return new Boolean(((Integer)value).intValue() != 0).toString(); + case CodeConstants.TYPE_CHAR: + Integer val = (Integer)value; + String ret = escapes.get(val); + if(ret == null) { + char c = (char)val.intValue(); + if(c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { + ret = String.valueOf(c); + } else { + ret = InterpreterUtil.charToUnicodeLiteral(c); + } + } + return "\'"+ret+"\'"; + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + int ival = ((Integer)value).intValue(); + + String intfield; + if(literal) { + return value.toString(); + } else if(ival == Integer.MAX_VALUE) { + intfield = "MAX_VALUE"; + } else if(ival == Integer.MIN_VALUE) { + intfield = "MIN_VALUE"; + } else { + return value.toString(); + } + return new FieldExprent(intfield, "java/lang/Integer", true, null, FieldDescriptor.INTEGER_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_LONG: + long lval = ((Long)value).longValue(); + + String longfield; + if(literal) { + return value.toString()+"L"; + } else if(lval == Long.MAX_VALUE) { + longfield = "MAX_VALUE"; + } else if(lval == Long.MIN_VALUE) { + longfield = "MIN_VALUE"; + } else { + return value.toString()+"L"; + } + return new FieldExprent(longfield, "java/lang/Long", true, null, FieldDescriptor.LONG_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_DOUBLE: + double dval = ((Double)value).doubleValue(); + + String doublefield; + if(literal) { + if(Double.isNaN(dval)) { + return "0.0D / 0.0"; + } else if(dval == Double.POSITIVE_INFINITY) { + return "1.0D / 0.0"; + } else if(dval == Double.NEGATIVE_INFINITY) { + return "-1.0D / 0.0"; + } else { + return value.toString() + "D"; + } + } else if(Double.isNaN(dval)) { + doublefield = "NaN"; + } else if(dval == Double.POSITIVE_INFINITY) { + doublefield = "POSITIVE_INFINITY"; + } else if(dval == Double.NEGATIVE_INFINITY) { + doublefield = "NEGATIVE_INFINITY"; + } else if(dval == Double.MAX_VALUE) { + doublefield = "MAX_VALUE"; + } else if(dval == Double.MIN_VALUE) { + doublefield = "MIN_VALUE"; + } else { + return value.toString()+"D"; + } + return new FieldExprent(doublefield, "java/lang/Double", true, null, FieldDescriptor.DOUBLE_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_FLOAT: + float fval = ((Float)value).floatValue(); + + String floatfield; + if(literal) { + if(Double.isNaN(fval)) { + return "0.0F / 0.0"; + } else if(fval == Double.POSITIVE_INFINITY) { + return "1.0F / 0.0"; + } else if(fval == Double.NEGATIVE_INFINITY) { + return "-1.0F / 0.0"; + } else { + return value.toString() + "F"; + } + } else if(Float.isNaN(fval)) { + floatfield = "NaN"; + } else if(fval == Float.POSITIVE_INFINITY) { + floatfield = "POSITIVE_INFINITY"; + } else if(fval == Float.NEGATIVE_INFINITY) { + floatfield = "NEGATIVE_INFINITY"; + } else if(fval == Float.MAX_VALUE) { + floatfield = "MAX_VALUE"; + } else if(fval == Float.MIN_VALUE) { + floatfield = "MIN_VALUE"; + } else { + return value.toString()+"F"; + } + return new FieldExprent(floatfield, "java/lang/Float", true, null, FieldDescriptor.FLOAT_DESCRIPTOR).toJava(0); + case CodeConstants.TYPE_NULL: + return "null"; + case CodeConstants.TYPE_OBJECT: + if(consttype.equals(VarType.VARTYPE_STRING)) { + return "\""+convertStringToJava(value.toString(), ascii)+"\""; + } else if(consttype.equals(VarType.VARTYPE_CLASS)) { + String strval = value.toString(); + + VarType classtype; + if(strval.startsWith("[")) { // array of simple type + classtype = new VarType(strval, false); + } else { // class + classtype = new VarType(strval, true); + } + + return ExprProcessor.getCastTypeName(classtype)+".class"; + } + } + } + + throw new RuntimeException("invalid constant type"); + } + + private String convertStringToJava(String value, boolean ascii) { + char[] arr = value.toCharArray(); + StringBuilder buffer = new StringBuilder(arr.length); + + for(char c: arr){ + switch(c) { + case '\\': // u005c: backslash \ + buffer.append("\\\\"); + break; + case 0x8: // "\\\\b"); // u0008: backspace BS + buffer.append("\\b"); + break; + case 0x9: //"\\\\t"); // u0009: horizontal tab HT + buffer.append("\\t"); + break; + case 0xA: //"\\\\n"); // u000a: linefeed LF + buffer.append("\\n"); + break; + case 0xC: //"\\\\f"); // u000c: form feed FF + buffer.append("\\f"); + break; + case 0xD: //"\\\\r"); // u000d: carriage return CR + buffer.append("\\r"); + break; + case 0x22: //"\\\\\""); // u0022: double quote " + buffer.append("\\\""); + break; + case 0x27: //"\\\\'"); // u0027: single quote ' + buffer.append("\\\'"); + break; + default: + if(c >= 32 && c < 127 || !ascii && InterpreterUtil.isPrintableUnicode(c)) { + buffer.append(c); + } else { + buffer.append(InterpreterUtil.charToUnicodeLiteral(c)); + } + } + } + + return buffer.toString(); + } + + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof ConstExprent)) return false; + + ConstExprent cn = (ConstExprent)o; + return InterpreterUtil.equalObjects(consttype, cn.getConsttype()) && + InterpreterUtil.equalObjects(value, cn.getValue()); + } + + public boolean hasBooleanValue() { + + switch(consttype.type) { + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + Integer ival = (Integer)value; + return ival.intValue() == 0 || + (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && ival.intValue() == 1); + } + + return false; + } + + public boolean hasValueOne() { + + switch(consttype.type) { + case CodeConstants.TYPE_BOOLEAN: + case CodeConstants.TYPE_CHAR: + case CodeConstants.TYPE_BYTE: + case CodeConstants.TYPE_BYTECHAR: + case CodeConstants.TYPE_SHORT: + case CodeConstants.TYPE_SHORTCHAR: + case CodeConstants.TYPE_INT: + return ((Integer)value).intValue() == 1; + case CodeConstants.TYPE_LONG: + return ((Long)value).intValue() == 1; + case CodeConstants.TYPE_DOUBLE: + return ((Double)value).intValue() == 1; + case CodeConstants.TYPE_FLOAT: + return ((Float)value).intValue() == 1; + } + + return false; + } + + public static ConstExprent getZeroConstant(int type) { + + switch(type) { + case CodeConstants.TYPE_INT: + return new ConstExprent(VarType.VARTYPE_INT, new Integer(0)); + case CodeConstants.TYPE_LONG: + return new ConstExprent(VarType.VARTYPE_LONG, new Long(0)); + case CodeConstants.TYPE_DOUBLE: + return new ConstExprent(VarType.VARTYPE_DOUBLE, new Double(0)); + case CodeConstants.TYPE_FLOAT: + return new ConstExprent(VarType.VARTYPE_FLOAT, new Float(0)); + } + + throw new RuntimeException("Invalid argument!"); + } + + public VarType getConsttype() { + return consttype; + } + + public void setConsttype(VarType consttype) { + this.consttype = consttype; + } + + public Object getValue() { + return value; + } + + public int getIntValue() { + return ((Integer)value).intValue(); + } + + public boolean isBoolPermitted() { + return boolPermitted; + } + + public void setBoolPermitted(boolean boolPermitted) { + this.boolPermitted = boolPermitted; + } + + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java new file mode 100644 index 0000000..ceaae30 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java @@ -0,0 +1,149 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class ExitExprent extends Exprent { + + public static final int EXIT_RETURN = 0; + public static final int EXIT_THROW = 1; + + // return or throw statement + private int exittype; + + private Exprent value; + + private VarType rettype; + + { + this.type = EXPRENT_EXIT; + } + + public ExitExprent(int exittype, Exprent value, VarType rettype) { + this.exittype = exittype; + this.value = value; + this.rettype = rettype; + } + + public Exprent copy() { + return new ExitExprent(exittype, value==null?null:value.copy(), rettype); + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + if(exittype == EXIT_RETURN && rettype.type!=CodeConstants.TYPE_VOID) { + result.addMinTypeExprent(value, VarType.getMinTypeInFamily(rettype.type_family)); + result.addMaxTypeExprent(value, rettype); + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if(value != null) { + lst.add(value); + } + return lst; + } + + public String toJava(int indent) { + if(exittype == EXIT_RETURN) { + StringBuilder buffer = new StringBuilder(); + + if(rettype.type!=CodeConstants.TYPE_VOID) { + buffer.append(" "); + ExprProcessor.getCastedExprent(value, rettype, buffer, indent, false); + } + + return "return"+buffer.toString(); + } else { + + MethodWrapper meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)); + + if(meth != null && node != null) { + StructExceptionsAttribute attr = (StructExceptionsAttribute)meth.methodStruct.getAttributes().getWithKey("Exceptions"); + + if(attr != null) { + String classname = null; + + for(int i=0;i<attr.getThrowsExceptions().size();i++) { + String excclassname = attr.getExcClassname(i, node.classStruct.getPool()); + if("java/lang/Throwable".equals(excclassname)) { + classname = excclassname; + break; + } else if("java/lang/Exception".equals(excclassname)) { + classname = excclassname; + } + } + + if(classname != null) { + VarType exctype = new VarType(classname, true); + + StringBuilder buffer = new StringBuilder(); + ExprProcessor.getCastedExprent(value, exctype, buffer, indent, false); + + return "throw "+buffer.toString(); + } + } + } + + return "throw "+value.toJava(indent); + } + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof ExitExprent)) return false; + + ExitExprent et = (ExitExprent)o; + return exittype==et.getExittype() && + InterpreterUtil.equalObjects(value, et.getValue()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == value) { + value = newexpr; + } + } + + public int getExittype() { + return exittype; + } + + public Exprent getValue() { + return value; + } + + public VarType getRettype() { + return rettype; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java new file mode 100644 index 0000000..6e15170 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -0,0 +1,134 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.gen.VarType; + + + +public class Exprent { + + public static final int MULTIPLE_USES = 1; + public static final int SIDE_EFFECTS_FREE = 2; + public static final int BOTH_FLAGS = 3; + + + public static final int EXPRENT_ARRAY = 1; + public static final int EXPRENT_ASSIGNMENT = 2; + public static final int EXPRENT_CONST = 3; + public static final int EXPRENT_EXIT = 4; + public static final int EXPRENT_FIELD = 5; + public static final int EXPRENT_FUNCTION = 6; + public static final int EXPRENT_IF = 7; + public static final int EXPRENT_INVOCATION = 8; + public static final int EXPRENT_MONITOR = 9; + public static final int EXPRENT_NEW = 10; + public static final int EXPRENT_SWITCH = 11; + public static final int EXPRENT_VAR = 12; + public static final int EXPRENT_ANNOTATION = 13; + public static final int EXPRENT_ASSERT = 14; + + public int type; + + public int id; + + { + // set exprent id + id = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.EXPRENT_COUNTER); + } + + public int getPrecedence() { + return 0; // the highest precedence + } + + public VarType getExprType() { + return VarType.VARTYPE_VOID; + } + + public int getExprentUse() { + return 0; + } + + public CheckTypesResult checkExprTypeBounds() { + return new CheckTypesResult(); + } + + public boolean containsExprent(Exprent exprent) { + + List<Exprent> listTemp = new ArrayList<Exprent>(getAllExprents(true)); + listTemp.add(this); + + for(Exprent lstexpr : listTemp) { + if(lstexpr.equals(exprent)) { + return true; + } + } + + return false; + } + + public List<Exprent> getAllExprents(boolean recursive) { + List<Exprent> lst = getAllExprents(); + + if(recursive) { + for(int i=lst.size()-1;i>=0;i--) { + lst.addAll(lst.get(i).getAllExprents(true)); + } + } + + return lst; + } + + public Set<VarVersionPaar> getAllVariables() { + + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(); + + List<Exprent> lstAllExprents = getAllExprents(true); + lstAllExprents.add(this); + + for(Exprent expr : lstAllExprents) { + if(expr.type == Exprent.EXPRENT_VAR) { + set.add(new VarVersionPaar((VarExprent)expr)); + } + } + + return set; + } + + public List<Exprent> getAllExprents() { + throw new RuntimeException("not implemented"); + } + + public Exprent copy() { + throw new RuntimeException("not implemented"); + } + + public String toJava(int indent) { + throw new RuntimeException("not implemented"); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) {} + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java new file mode 100644 index 0000000..235ff6f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -0,0 +1,195 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class FieldExprent extends Exprent { + + private String name; + + private String classname; + + private boolean isStatic; + + private Exprent instance; + + private FieldDescriptor descriptor; + + { + this.type = EXPRENT_FIELD; + } + + public FieldExprent(LinkConstant cn, Exprent instance) { + + this.instance = instance; + + if(instance == null) { + isStatic = true; + } + + classname = cn.classname; + name = cn.elementname; + descriptor = FieldDescriptor.parseDescriptor(cn.descriptor); + } + + public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor) { + this.name = name; + this.classname = classname; + this.isStatic = isStatic; + this.instance = instance; + this.descriptor = descriptor; + } + + public VarType getExprType() { + return descriptor.type; + } + + public int getExprentUse() { + if(instance == null) { + return Exprent.MULTIPLE_USES; + } else { + return instance.getExprentUse() & Exprent.MULTIPLE_USES; + } + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if(instance != null) { + lst.add(instance); + } + return lst; + } + + public Exprent copy() { + return new FieldExprent(name, classname, isStatic, instance==null?null:instance.copy(), descriptor); + } + + public String toJava(int indent) { + StringBuffer buf = new StringBuffer(); + + + if(isStatic) { + ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + if(node == null || !classname.equals(node.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + buf.append("."); + } + } else { + + String super_qualifier = null; + + if(instance != null && instance.type == Exprent.EXPRENT_VAR) { + VarExprent instvar = (VarExprent)instance; + VarVersionPaar varpaar = new VarVersionPaar(instvar); + + MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + + if(current_meth != null) { // FIXME: remove + String this_classname = current_meth.varproc.getThisvars().get(varpaar); + + if(this_classname != null) { + if(!classname.equals(this_classname)) { // TODO: direct comparison to the super class? + super_qualifier = this_classname; + } + } + } + } + + if(super_qualifier != null) { + StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; + + if(!super_qualifier.equals(current_class.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); + buf.append("."); + } + buf.append("super"); + } else { + StringBuilder buff = new StringBuilder(); + boolean casted = ExprProcessor.getCastedExprent(instance, new VarType(CodeConstants.TYPE_OBJECT, 0, classname), buff, indent, true); + String res = buff.toString(); + + if(casted || instance.getPrecedence() > getPrecedence()) { + res = "("+res+")"; + } + + buf.append(res); + } + + if(buf.toString().equals(VarExprent.VAR_NAMELESS_ENCLOSURE)) { // FIXME: workaround for field access of an anonymous enclosing class. Find a better way. + buf.setLength(0); + } else { + buf.append("."); + } + } + + buf.append(name); + + return buf.toString(); + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof FieldExprent)) return false; + + FieldExprent ft = (FieldExprent)o; + return InterpreterUtil.equalObjects(name, ft.getName()) && + InterpreterUtil.equalObjects(classname, ft.getClassname()) && + isStatic == ft.isStatic() && + InterpreterUtil.equalObjects(instance, ft.getInstance()) && + InterpreterUtil.equalObjects(descriptor, ft.getDescriptor()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == instance) { + instance = newexpr; + } + } + + public String getClassname() { + return classname; + } + + public FieldDescriptor getDescriptor() { + return descriptor; + } + + public Exprent getInstance() { + return instance; + } + + public boolean isStatic() { + return isStatic; + } + + public String getName() { + return name; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java new file mode 100644 index 0000000..e4c942f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java @@ -0,0 +1,585 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.ListStack; + + + +public class FunctionExprent extends Exprent { + + public static final int FUNCTION_ADD = 0; + public static final int FUNCTION_SUB = 1; + public static final int FUNCTION_MUL = 2; + public static final int FUNCTION_DIV = 3; + + public static final int FUNCTION_AND = 4; + public static final int FUNCTION_OR = 5; + public static final int FUNCTION_XOR = 6; + + public static final int FUNCTION_REM = 7; + + public static final int FUNCTION_SHL = 8; + public static final int FUNCTION_SHR = 9; + public static final int FUNCTION_USHR = 10; + + public static final int FUNCTION_BITNOT = 11; + public static final int FUNCTION_BOOLNOT = 12; + public static final int FUNCTION_NEG = 13; + + public final static int FUNCTION_I2L = 14; + public final static int FUNCTION_I2F = 15; + public final static int FUNCTION_I2D = 16; + public final static int FUNCTION_L2I = 17; + public final static int FUNCTION_L2F = 18; + public final static int FUNCTION_L2D = 19; + public final static int FUNCTION_F2I = 20; + public final static int FUNCTION_F2L = 21; + public final static int FUNCTION_F2D = 22; + public final static int FUNCTION_D2I = 23; + public final static int FUNCTION_D2L = 24; + public final static int FUNCTION_D2F = 25; + public final static int FUNCTION_I2B = 26; + public final static int FUNCTION_I2C = 27; + public final static int FUNCTION_I2S = 28; + + public final static int FUNCTION_CAST = 29; + public final static int FUNCTION_INSTANCEOF = 30; + + public final static int FUNCTION_ARRAYLENGTH = 31; + + public final static int FUNCTION_IMM = 32; + public final static int FUNCTION_MMI = 33; + + public final static int FUNCTION_IPP = 34; + public final static int FUNCTION_PPI = 35; + + public final static int FUNCTION_IIF = 36; + + public final static int FUNCTION_LCMP = 37; + public final static int FUNCTION_FCMPL = 38; + public final static int FUNCTION_FCMPG = 39; + public final static int FUNCTION_DCMPL = 40; + public final static int FUNCTION_DCMPG = 41; + + public static final int FUNCTION_EQ = 42; + public static final int FUNCTION_NE = 43; + public static final int FUNCTION_LT = 44; + public static final int FUNCTION_GE = 45; + public static final int FUNCTION_GT = 46; + public static final int FUNCTION_LE = 47; + + public static final int FUNCTION_CADD = 48; + public static final int FUNCTION_COR = 49; + + public static final int FUNCTION_STRCONCAT = 50; + + private static final VarType[] types = new VarType[] { + VarType.VARTYPE_LONG, + VarType.VARTYPE_FLOAT, + VarType.VARTYPE_DOUBLE, + VarType.VARTYPE_INT, + VarType.VARTYPE_FLOAT, + VarType.VARTYPE_DOUBLE, + VarType.VARTYPE_INT, + VarType.VARTYPE_LONG, + VarType.VARTYPE_DOUBLE, + VarType.VARTYPE_INT, + VarType.VARTYPE_LONG, + VarType.VARTYPE_FLOAT, + VarType.VARTYPE_BYTE, + VarType.VARTYPE_CHAR, + VarType.VARTYPE_SHORT + }; + + private static final String[] operators = new String[] { + " + ", + " - ", + " * ", + " / ", + " & ", + " | ", + " ^ ", + " % ", + " << ", + " >> ", + " >>> ", + " == ", + " != ", + " < ", + " >= ", + " > ", + " <= ", + " && ", + " || ", + " + " + }; + + private static final int[] precedence = new int[] { + 3, // FUNCTION_ADD + 3, // FUNCTION_SUB + 2, // FUNCTION_MUL + 2, // FUNCTION_DIV + 7, // FUNCTION_AND + 9, // FUNCTION_OR + 8, // FUNCTION_XOR + 2, // FUNCTION_REM + 4, // FUNCTION_SHL + 4, // FUNCTION_SHR + 4, // FUNCTION_USHR + 1, // FUNCTION_BITNOT + 1, // FUNCTION_BOOLNOT + 1, // FUNCTION_NEG + 1, // FUNCTION_I2L + 1, // FUNCTION_I2F + 1, // FUNCTION_I2D + 1, // FUNCTION_L2I + 1, // FUNCTION_L2F + 1, // FUNCTION_L2D + 1, // FUNCTION_F2I + 1, // FUNCTION_F2L + 1, // FUNCTION_F2D + 1, // FUNCTION_D2I + 1, // FUNCTION_D2L + 1, // FUNCTION_D2F + 1, // FUNCTION_I2B + 1, // FUNCTION_I2C + 1, // FUNCTION_I2S + 1, // FUNCTION_CAST + 6, // FUNCTION_INSTANCEOF + 0, // FUNCTION_ARRAYLENGTH + 1, // FUNCTION_IMM + 1, // FUNCTION_MMI + 1, // FUNCTION_IPP + 1, // FUNCTION_PPI + 12, // FUNCTION_IFF + -1, // FUNCTION_LCMP + -1, // FUNCTION_FCMPL + -1, // FUNCTION_FCMPG + -1, // FUNCTION_DCMPL + -1, // FUNCTION_DCMPG + 6, // FUNCTION_EQ = 41; + 6, // FUNCTION_NE = 42; + 5, // FUNCTION_LT = 43; + 5, // FUNCTION_GE = 44; + 5, // FUNCTION_GT = 45; + 5, // FUNCTION_LE = 46; + 10, // FUNCTION_CADD = 47; + 11, // FUNCTION_COR = 48; + 3 // FUNCTION_STRCONCAT = 49; + }; + + private static final HashSet<Integer> associativity = new HashSet<Integer>(Arrays.asList(new Integer[]{FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, + FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STRCONCAT})); + + private int functype; + + private VarType implicitType; + + private List<Exprent> lstOperands = new ArrayList<Exprent>(); + + { + this.type = EXPRENT_FUNCTION; + } + + public FunctionExprent(int functype, ListStack<Exprent> stack) { + this.functype = functype; + if(functype>=FUNCTION_BITNOT && functype<=FUNCTION_PPI && functype!=FUNCTION_CAST + && functype!=FUNCTION_INSTANCEOF) { + lstOperands.add(stack.pop()); + } else if(functype == FUNCTION_IIF) { + throw new RuntimeException("no direct instantiation possible"); + } else { + Exprent expr = stack.pop(); + lstOperands.add(stack.pop()); + lstOperands.add(expr); + } + } + + public FunctionExprent(int functype, List<Exprent> operands) { + this.functype = functype; + this.lstOperands = operands; + } + + public VarType getExprType() { + VarType exprType = null; + + if(functype <= FUNCTION_NEG || functype == FUNCTION_IPP || functype == FUNCTION_PPI + || functype == FUNCTION_IMM || functype == FUNCTION_MMI) { + + VarType type1 = lstOperands.get(0).getExprType(); + VarType type2 = null; + if(lstOperands.size() > 1) { + type2 = lstOperands.get(1).getExprType(); + } + + switch(functype) { + case FUNCTION_IMM: + case FUNCTION_MMI: + case FUNCTION_IPP: + case FUNCTION_PPI: + exprType = implicitType; + break; + case FUNCTION_BOOLNOT: + exprType = VarType.VARTYPE_BOOLEAN; + break; + case FUNCTION_SHL: + case FUNCTION_SHR: + case FUNCTION_USHR: + case FUNCTION_BITNOT: + case FUNCTION_NEG: + exprType = getMaxVarType(new VarType[]{type1}); + break; + case FUNCTION_ADD: + case FUNCTION_SUB: + case FUNCTION_MUL: + case FUNCTION_DIV: + case FUNCTION_REM: + exprType = getMaxVarType(new VarType[]{type1, type2}); + break; + case FUNCTION_AND: + case FUNCTION_OR: + case FUNCTION_XOR: + if(type1.type == CodeConstants.TYPE_BOOLEAN & type2.type == CodeConstants.TYPE_BOOLEAN) { + exprType = VarType.VARTYPE_BOOLEAN; + } else { + exprType = getMaxVarType(new VarType[]{type1, type2}); + } + } + } else if(functype == FUNCTION_CAST){ + exprType = lstOperands.get(1).getExprType(); + } else if(functype == FUNCTION_IIF){ + Exprent param1 = lstOperands.get(1); + Exprent param2 = lstOperands.get(2); + VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType()); + + if(param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST && + supertype.type != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) { + exprType = VarType.VARTYPE_INT; + } else { + exprType = supertype; + } + } else if(functype == FUNCTION_STRCONCAT){ + exprType = VarType.VARTYPE_STRING; + } else if(functype >= FUNCTION_EQ){ + exprType = VarType.VARTYPE_BOOLEAN; + } else if(functype == FUNCTION_INSTANCEOF) { + exprType = VarType.VARTYPE_BOOLEAN; + } else if(functype >= FUNCTION_ARRAYLENGTH) { + exprType = VarType.VARTYPE_INT; + } else { + exprType = types[functype - FUNCTION_I2L]; + } + + return exprType; + } + + public int getExprentUse() { + + if(functype >= FUNCTION_IMM && functype <= FUNCTION_PPI) { + return 0; + } else { + int ret = Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; + for(Exprent expr: lstOperands) { + ret &= expr.getExprentUse(); + } + return ret; + } + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + Exprent param1 = lstOperands.get(0); + VarType type1 = param1.getExprType(); + Exprent param2 = null; + VarType type2 = null; + + if(lstOperands.size() > 1) { + param2 = lstOperands.get(1); + type2 = param2.getExprType(); + } + + switch(functype) { + case FUNCTION_IIF: + VarType supertype = getExprType(); + if(supertype == null) { + supertype = getExprType(); + } + result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN); + result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.type_family)); + result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.type_family)); + break; + case FUNCTION_I2L: + case FUNCTION_I2F: + case FUNCTION_I2D: + case FUNCTION_I2B: + case FUNCTION_I2C: + case FUNCTION_I2S: + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(param1, VarType.VARTYPE_INT); + break; + case FUNCTION_IMM: + case FUNCTION_IPP: + case FUNCTION_MMI: + case FUNCTION_PPI: + result.addMinTypeExprent(param1, implicitType); + result.addMaxTypeExprent(param1, implicitType); + break; + case FUNCTION_ADD: + case FUNCTION_SUB: + case FUNCTION_MUL: + case FUNCTION_DIV: + case FUNCTION_REM: + case FUNCTION_SHL: + case FUNCTION_SHR: + case FUNCTION_USHR: + case FUNCTION_LT: + case FUNCTION_GE: + case FUNCTION_GT: + case FUNCTION_LE: + result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); + case FUNCTION_BITNOT: + // case FUNCTION_BOOLNOT: + case FUNCTION_NEG: + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + break; + case FUNCTION_AND: + case FUNCTION_OR: + case FUNCTION_XOR: + case FUNCTION_EQ: + case FUNCTION_NE: + { + if(type1.type == CodeConstants.TYPE_BOOLEAN) { + + if(type2.isStrictSuperset(type1)) { + + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + + } else { // both are booleans + + boolean param1_false_boolean = type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue()); + boolean param2_false_boolean = type1.isFalseBoolean() || (param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue()); + + if(param1_false_boolean || param2_false_boolean) { + result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); + result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); + } + } + + } else if(type2.type == CodeConstants.TYPE_BOOLEAN) { + + if(type1.isStrictSuperset(type2)) { + result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); + } + } + } + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.addAll(lstOperands); + return lst; + } + + public Exprent copy() { + List<Exprent> lst = new ArrayList<Exprent>(); + for(Exprent expr: lstOperands) { + lst.add(expr.copy()); + } + FunctionExprent func = new FunctionExprent(functype, lst); + func.setImplicitType(implicitType); + + return func; + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof FunctionExprent)) return false; + + FunctionExprent fe = (FunctionExprent)o; + return functype==fe.getFunctype() && + InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + for(int i=0;i<lstOperands.size();i++) { + if(oldexpr == lstOperands.get(i)) { + lstOperands.set(i, newexpr); + } + } + } + + public String toJava(int indent) { + + if(functype <= FUNCTION_USHR) { + return wrapOperandString(lstOperands.get(0), false, indent)+operators[functype]+ + wrapOperandString(lstOperands.get(1), true, indent); + } + + if(functype >= FUNCTION_EQ) { + return wrapOperandString(lstOperands.get(0), false, indent)+operators[functype-FUNCTION_EQ+11]+ + wrapOperandString(lstOperands.get(1), true, indent); + } + + switch(functype) { + case FUNCTION_BITNOT: + return "~"+wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_BOOLNOT: + return "!"+wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_NEG: + return "-"+wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_CAST: + return "("+lstOperands.get(1).toJava(indent)+")"+wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_ARRAYLENGTH: + Exprent arr = lstOperands.get(0); + + String res = wrapOperandString(arr, false, indent); + if(arr.getExprType().arraydim == 0) { + VarType objarr = VarType.VARTYPE_OBJECT.copy(); + objarr.arraydim = 1; // type family does not change + + res = "(("+ExprProcessor.getCastTypeName(objarr)+")"+res+")"; + } + return res+".length"; + case FUNCTION_IIF: + return wrapOperandString(lstOperands.get(0), true, indent)+"?"+wrapOperandString(lstOperands.get(1), true, indent)+":"+ + wrapOperandString(lstOperands.get(2), true, indent); + case FUNCTION_IPP: + return wrapOperandString(lstOperands.get(0), true, indent)+"++"; + case FUNCTION_PPI: + return "++"+wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_IMM: + return wrapOperandString(lstOperands.get(0), true, indent)+"--"; + case FUNCTION_MMI: + return "--"+wrapOperandString(lstOperands.get(0), true, indent); + case FUNCTION_INSTANCEOF: + return wrapOperandString(lstOperands.get(0), true, indent)+" instanceof "+wrapOperandString(lstOperands.get(1), true, indent); + case FUNCTION_LCMP: // shouldn't appear in the final code + return "__lcmp__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; + case FUNCTION_FCMPL: // shouldn't appear in the final code + return "__fcmpl__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; + case FUNCTION_FCMPG: // shouldn't appear in the final code + return "__fcmpg__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; + case FUNCTION_DCMPL: // shouldn't appear in the final code + return "__dcmpl__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; + case FUNCTION_DCMPG: // shouldn't appear in the final code + return "__dcmpg__("+wrapOperandString(lstOperands.get(0), true, indent)+","+wrapOperandString(lstOperands.get(1), true, indent)+")"; + } + + if(functype <= FUNCTION_I2S) { + return "("+ExprProcessor.getTypeName(types[functype - FUNCTION_I2L])+")"+wrapOperandString(lstOperands.get(0), true, indent); + } + +// return "<unknown function>"; + throw new RuntimeException("invalid function"); + } + + public int getPrecedence() { + return getPrecedence(functype); + } + + public static int getPrecedence(int func) { + return precedence[func]; + } + + public VarType getSimpleCastType() { + return types[functype - FUNCTION_I2L]; + } + + private String wrapOperandString(Exprent expr, boolean eq, int indent) { + + int myprec = getPrecedence(); + int exprprec = expr.getPrecedence(); + + boolean parentheses = exprprec > myprec; + if(!parentheses && eq) { + parentheses = (exprprec == myprec); + if(parentheses) { + if(expr.type == Exprent.EXPRENT_FUNCTION && + ((FunctionExprent)expr).getFunctype() == functype) { + parentheses = !associativity.contains(functype); + } + } + } + + String res = expr.toJava(indent); + + if(parentheses) { + res = "("+res+")"; + } + + return res; + } + + private VarType getMaxVarType(VarType[] arr) { + + int[] types = new int[] {CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG}; + VarType[] vartypes = new VarType[] {VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG}; + + for(int i=0;i<types.length;i++) { + for(int j=0;j<arr.length;j++) { + if(arr[j].type == types[i]) { + return vartypes[i]; + } + } + } + + return VarType.VARTYPE_INT; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public int getFunctype() { + return functype; + } + + public void setFunctype(int functype) { + this.functype = functype; + } + + public List<Exprent> getLstOperands() { + return lstOperands; + } + + public void setLstOperands(List<Exprent> lstOperands) { + this.lstOperands = lstOperands; + } + + public VarType getImplicitType() { + return implicitType; + } + + public void setImplicitType(VarType implicitType) { + this.implicitType = implicitType; + } + +} + diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java new file mode 100644 index 0000000..15e39ba --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java @@ -0,0 +1,146 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.ListStack; + + +public class IfExprent extends Exprent { + + public static final int IF_EQ = 0; + public static final int IF_NE = 1; + public static final int IF_LT = 2; + public static final int IF_GE = 3; + public static final int IF_GT = 4; + public static final int IF_LE = 5; + + public static final int IF_NULL = 6; + public static final int IF_NONNULL = 7; + + public static final int IF_ICMPEQ = 8; + public static final int IF_ICMPNE = 9; + public static final int IF_ICMPLT = 10; + public static final int IF_ICMPGE = 11; + public static final int IF_ICMPGT = 12; + public static final int IF_ICMPLE = 13; + public static final int IF_ACMPEQ = 14; + public static final int IF_ACMPNE = 15; + + public static final int IF_CAND = 16; + public static final int IF_COR = 17; + + public static final int IF_NOT = 18; + public static final int IF_VALUE = 19; + + private static final int[] functypes = new int[] { + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_CADD, + FunctionExprent.FUNCTION_COR, + FunctionExprent.FUNCTION_BOOLNOT, + -1 + }; + + private Exprent condition; + + { + this.type = EXPRENT_IF; + } + + public IfExprent(int iftype, ListStack<Exprent> stack) { + + if(iftype <= IF_LE) { + stack.push(new ConstExprent(0, true)); + } else if(iftype <= IF_NONNULL) { + stack.push(new ConstExprent(VarType.VARTYPE_NULL, null)); + } + + if(iftype == IF_VALUE) { + condition = stack.pop(); + } else { + condition = new FunctionExprent(functypes[iftype], stack); + } + } + + private IfExprent(Exprent condition) { + this.condition = condition; + } + + public Exprent copy() { + return new IfExprent(condition.copy()); + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(condition); + return lst; + } + + public String toJava(int indent) { + StringBuffer buf = new StringBuffer("if("); + buf.append(condition.toJava(indent)); + buf.append(")"); + + return buf.toString(); + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof IfExprent)) return false; + + IfExprent ie = (IfExprent)o; + return InterpreterUtil.equalObjects(condition, ie.getCondition()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == condition) { + condition = newexpr; + } + } + + public IfExprent negateIf() { + condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{condition})); + return this; + } + + public Exprent getCondition() { + return condition; + } + + public void setCondition(Exprent condition) { + this.condition = condition; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java new file mode 100644 index 0000000..835e96e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -0,0 +1,504 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.*; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.ListStack; + + +public class InvocationExprent extends Exprent { + + public static final int INVOKE_SPECIAL = 1; + public static final int INVOKE_VIRTUAL = 2; + public static final int INVOKE_STATIC = 3; + public static final int INVOKE_INTERFACE = 4; + public static final int INVOKE_DYNAMIC = 5; + + public static final int TYP_GENERAL = 1; + public static final int TYP_INIT = 2; + public static final int TYP_CLINIT = 3; + + public static final int CONSTRUCTOR_NOT = 0; + public static final int CONSTRUCTOR_THIS = 1; + public static final int CONSTRUCTOR_SUPER = 2; + + private String name; + + private String classname; + + private boolean isStatic; + + private int functype = TYP_GENERAL; + + private Exprent instance; + + private MethodDescriptor descriptor; + + private String stringDescriptor; + + private String invoke_dynamic_classsuffix; + + private int invocationTyp = INVOKE_VIRTUAL; + + private List<Exprent> lstParameters = new ArrayList<Exprent>(); + + { + this.type = EXPRENT_INVOCATION; + } + + public InvocationExprent() {} + + public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack, int dynamic_invokation_type) { + + name = cn.elementname; + classname = cn.classname; + + switch(opcode) { + case CodeConstants.opc_invokestatic: + invocationTyp = INVOKE_STATIC; + break; + case CodeConstants.opc_invokespecial: + invocationTyp = INVOKE_SPECIAL; + break; + case CodeConstants.opc_invokevirtual: + invocationTyp = INVOKE_VIRTUAL; + break; + case CodeConstants.opc_invokeinterface: + invocationTyp = INVOKE_INTERFACE; + break; + case CodeConstants.opc_invokedynamic: + invocationTyp = INVOKE_DYNAMIC; + + classname = "java/lang/Class"; // dummy class name + invoke_dynamic_classsuffix = "##Lambda_" + cn.index1 + "_" + cn.index2; + + } + + if("<init>".equals(name)) { + functype = TYP_INIT; + }else if("<clinit>".equals(name)) { + functype = TYP_CLINIT; + } + + stringDescriptor = cn.descriptor; + descriptor = MethodDescriptor.parseDescriptor(cn.descriptor); + + for(int i=0;i<descriptor.params.length;i++) { + lstParameters.add(0, stack.pop()); + } + + if(opcode == CodeConstants.opc_invokedynamic) { + if(dynamic_invokation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) { + isStatic = true; + } else { + instance = lstParameters.get(0); // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method. + } + } else if(opcode == CodeConstants.opc_invokestatic) { + isStatic = true; + } else { + instance = stack.pop(); + } + } + + private InvocationExprent(InvocationExprent expr) { + name = expr.getName(); + classname = expr.getClassname(); + isStatic = expr.isStatic(); + functype = expr.getFunctype(); + instance = expr.getInstance(); + if(instance != null) { + instance = instance.copy(); + } + invocationTyp = expr.getInvocationTyp(); + stringDescriptor = expr.getStringDescriptor(); + descriptor = expr.getDescriptor(); + lstParameters = new ArrayList<Exprent>(expr.getLstParameters()); + for(int i=0;i<lstParameters.size();i++) { + lstParameters.set(i, lstParameters.get(i).copy()); + } + } + + + public VarType getExprType() { + return descriptor.ret; + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + for(int i=0;i<lstParameters.size();i++) { + Exprent parameter = lstParameters.get(i); + + VarType leftType = descriptor.params[i]; + + result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.type_family)); + result.addMaxTypeExprent(parameter, leftType); + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if(instance != null) { + lst.add(instance); + } + lst.addAll(lstParameters); + return lst; + } + + + public Exprent copy() { + return new InvocationExprent(this); + } + + public String toJava(int indent) { + StringBuilder buf = new StringBuilder(""); + + String super_qualifier = null; + boolean isInstanceThis = false; + + if(invocationTyp == INVOKE_DYNAMIC) { +// ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); +// +// if(node != null) { +// ClassNode lambda_node = DecompilerContext.getClassprocessor().getMapRootClasses().get(node.classStruct.qualifiedName + invoke_dynamic_classsuffix); +// if(lambda_node != null) { +// +// String typename = ExprProcessor.getCastTypeName(lambda_node.anonimousClassType); +// +// StringWriter strwriter = new StringWriter(); +// BufferedWriter bufstrwriter = new BufferedWriter(strwriter); +// +// ClassWriter clwriter = new ClassWriter(); +// +// try { +// bufstrwriter.write("new " + typename + "() {"); +// bufstrwriter.newLine(); +// +// +// +// bufstrwriter.flush(); +// } catch(IOException ex) { +// throw new RuntimeException(ex); +// } +// +// buf.append(strwriter.toString()); +// +// } +// } + + } else if(isStatic) { + + ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE); + if(node == null || !classname.equals(node.classStruct.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(classname))); + } + + } else { + + if(instance != null && instance.type == Exprent.EXPRENT_VAR) { + VarExprent instvar = (VarExprent)instance; + VarVersionPaar varpaar = new VarVersionPaar(instvar); + + VarProcessor vproc = instvar.getProcessor(); + if(vproc == null) { + MethodWrapper current_meth = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); + if(current_meth != null) { + vproc = current_meth.varproc; + } + } + + String this_classname = null; + if(vproc != null) { + this_classname = vproc.getThisvars().get(varpaar); + } + + if(this_classname != null) { + isInstanceThis = true; + + if(invocationTyp == INVOKE_SPECIAL) { + if(!classname.equals(this_classname)) { // TODO: direct comparison to the super class? + super_qualifier = this_classname; + } + } + } + } + + if(functype == TYP_GENERAL){ + if(super_qualifier != null) { + StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; + + if(!super_qualifier.equals(current_class.qualifiedName)) { + buf.append(DecompilerContext.getImpcollector().getShortName(ExprProcessor.buildJavaClassName(super_qualifier))); + buf.append("."); + } + buf.append("super"); + } else { + String res = instance.toJava(indent); + + VarType rightType = instance.getExprType(); + VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, classname); + + if(rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) { + buf.append("(("+ExprProcessor.getCastTypeName(leftType)+")"); + + if(instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { + res = "("+res+")"; + } + buf.append(res+")"); + } else if(instance.getPrecedence() > getPrecedence()) { + buf.append("("+res+")"); + } else { + buf.append(res); + } + } + } + } + + switch(functype) { + case TYP_GENERAL: + if(VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) { + buf = new StringBuilder(""); + } + + if(buf.length() > 0) { + buf.append("."); + } + + buf.append(name); + if(invocationTyp == INVOKE_DYNAMIC) { + buf.append("<invokedynamic>"); + } + buf.append("("); + + break; + case TYP_CLINIT: + throw new RuntimeException("Explicite invocation of <clinit>"); + case TYP_INIT: + if(super_qualifier != null) { + buf.append("super("); + } else if(isInstanceThis) { + buf.append("this("); + } else { + buf.append(instance.toJava(indent)); + buf.append(".<init>("); +// throw new RuntimeException("Unrecognized invocation of <init>"); // FIXME: activate + } + } + + List<VarVersionPaar> sigFields = null; + boolean isEnum = false; + if(functype == TYP_INIT) { + ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); + + if(newnode != null) { // own class + if(newnode.wrapper != null) { + sigFields = newnode.wrapper.getMethodWrapper("<init>", stringDescriptor).signatureFields; + } else { + if(newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0) { // non-static member class + sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar)null)); + sigFields.set(0, new VarVersionPaar(-1, 0)); + } + } + isEnum = (newnode.classStruct.access_flags & CodeConstants.ACC_ENUM) != 0 && + DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); + } + } + + Set<Integer> setAmbiguousParameters = getAmbiguousParameters(); + + boolean firstpar = true; + int start = isEnum ? 2 : 0; + for(int i=start;i<lstParameters.size();i++) { + if(sigFields == null || sigFields.get(i) == null) { + if(!firstpar) { + buf.append(", "); + } + + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i)); + + buf.append(buff); + firstpar = false; + } + } + buf.append(")"); + + return buf.toString(); + } + + private Set<Integer> getAmbiguousParameters() { + + Set<Integer> ret = new HashSet<Integer>(); + + StructClass cstr = DecompilerContext.getStructcontext().getClass(classname); + if(cstr != null) { + List<MethodDescriptor> lstMethods = new ArrayList<MethodDescriptor>(); + for(StructMethod meth : cstr.getMethods()) { + if(name.equals(meth.getName())) { + MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.getDescriptor()); + if(md.params.length == descriptor.params.length) { + boolean equals = true; + for(int i=0;i<md.params.length;i++) { + if(md.params[i].type_family != descriptor.params[i].type_family) { + equals = false; + break; + } + } + + if(equals) { + lstMethods.add(md); + } + } + } + } + + if(lstMethods.size() > 1) { + for(int i=0;i<descriptor.params.length;i++) { + VarType partype = descriptor.params[i]; + + for(MethodDescriptor md : lstMethods) { + if(!partype.equals(md.params[i])) { + ret.add(i); + break; + } + } + } + } + } + + return ret; + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof InvocationExprent)) return false; + + InvocationExprent it = (InvocationExprent)o; + return InterpreterUtil.equalObjects(name, it.getName()) && + InterpreterUtil.equalObjects(classname, it.getClassname()) && + isStatic == it.isStatic() && + InterpreterUtil.equalObjects(instance, it.getInstance()) && + InterpreterUtil.equalObjects(descriptor, it.getDescriptor()) && + functype == it.getFunctype() && + InterpreterUtil.equalLists(lstParameters, it.getLstParameters()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == instance) { + instance = newexpr; + } + + for(int i=0;i<lstParameters.size();i++) { + if(oldexpr == lstParameters.get(i)) { + lstParameters.set(i, newexpr); + } + } + } + + public List<Exprent> getLstParameters() { + return lstParameters; + } + + public void setLstParameters(List<Exprent> lstParameters) { + this.lstParameters = lstParameters; + } + + public MethodDescriptor getDescriptor() { + return descriptor; + } + + public void setDescriptor(MethodDescriptor descriptor) { + this.descriptor = descriptor; + } + + public String getClassname() { + return classname; + } + + public void setClassname(String classname) { + this.classname = classname; + } + + public int getFunctype() { + return functype; + } + + public void setFunctype(int functype) { + this.functype = functype; + } + + public Exprent getInstance() { + return instance; + } + + public void setInstance(Exprent instance) { + this.instance = instance; + } + + public boolean isStatic() { + return isStatic; + } + + public void setStatic(boolean isStatic) { + this.isStatic = isStatic; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getStringDescriptor() { + return stringDescriptor; + } + + public void setStringDescriptor(String stringDescriptor) { + this.stringDescriptor = stringDescriptor; + } + + public int getInvocationTyp() { + return invocationTyp; + } + + public void setInvocationTyp(int invocationTyp) { + this.invocationTyp = invocationTyp; + } + + public String getInvokeDynamicClassSuffix() { + return invoke_dynamic_classsuffix; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java new file mode 100644 index 0000000..d231af0 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java @@ -0,0 +1,82 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class MonitorExprent extends Exprent { + + public static final int MONITOR_ENTER = 0; + public static final int MONITOR_EXIT = 1; + + private int montype; + + private Exprent value; + + { + this.type = EXPRENT_MONITOR; + } + + public MonitorExprent(int montype, Exprent value) { + this.montype = montype; + this.value = value; + } + + public Exprent copy() { + return new MonitorExprent(montype, value.copy()); + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(value); + return lst; + } + + public String toJava(int indent) { + if(montype == MONITOR_ENTER) { + return "synchronized("+value.toJava(indent)+")"; + } else { + return ""; + } + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof MonitorExprent)) return false; + + MonitorExprent me = (MonitorExprent)o; + return montype == me.getMontype() && + InterpreterUtil.equalObjects(value, me.getValue()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == value) { + value = newexpr; + } + } + + public int getMontype() { + return montype; + } + + public Exprent getValue() { + return value; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java new file mode 100644 index 0000000..0d1814a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -0,0 +1,512 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.ClassWriter; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.ListStack; + +public class NewExprent extends Exprent { + + private InvocationExprent constructor; + + private VarType newtype; + + private List<Exprent> lstDims = new ArrayList<Exprent>(); + + private List<Exprent> lstArrayElements = new ArrayList<Exprent>(); + + private boolean directArrayInit; + + private boolean anonymous; + + private boolean lambda; + + private boolean enumconst; + + { + this.type = EXPRENT_NEW; + } + + public NewExprent(VarType newtype, ListStack<Exprent> stack, int arraydim) { + this.newtype = newtype; + for(int i=0;i<arraydim;i++) { + lstDims.add(0, stack.pop()); + } + + setAnonymous(); + } + + public NewExprent(VarType newtype, List<Exprent> lstDims) { + this.newtype = newtype; + this.lstDims = lstDims; + + setAnonymous(); + } + + private void setAnonymous() { + + anonymous = false; + lambda = false; + + if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arraydim == 0) { + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + + if(node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) { + anonymous = true; + + if(node.type == ClassNode.CLASS_LAMBDA) { + lambda = true; + } + } + } + } + + public VarType getExprType() { + + if(anonymous) { + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + + return node.anonimousClassType; + } else { + return newtype; + } + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + if(newtype.arraydim != 0) { + for(Exprent dim: lstDims) { + result.addMinTypeExprent(dim, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(dim, VarType.VARTYPE_INT); + } + + if(newtype.arraydim == 1) { + + VarType leftType = newtype.copy(); + leftType.decArrayDim(); + + for(Exprent element: lstArrayElements) { + result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.type_family)); + result.addMaxTypeExprent(element, leftType); + } + } + } else { + if(constructor != null) { + return constructor.checkExprTypeBounds(); + } + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + if(newtype.arraydim == 0) { + if(constructor != null) { + Exprent constructor_instance = constructor.getInstance(); + + if(constructor_instance != null) { // should be true only for a lambda expression with a virtual content method + lst.add(constructor_instance); + } + + lst.addAll(constructor.getLstParameters()); + } + } else { + lst.addAll(lstDims); + lst.addAll(lstArrayElements); + } + + return lst; + } + + public Exprent copy() { + List<Exprent> lst = new ArrayList<Exprent>(); + for(Exprent expr: lstDims) { + lst.add(expr.copy()); + } + + NewExprent ret = new NewExprent(newtype, lst); + ret.setConstructor(constructor==null?null:(InvocationExprent)constructor.copy()); + ret.setLstArrayElements(lstArrayElements); + ret.setDirectArrayInit(directArrayInit); + ret.setAnonymous(anonymous); + ret.setEnumconst(enumconst); + return ret; + } + + public int getPrecedence() { + return 1; // precedence of new + } + + public String toJava(int indent) { + StringBuilder buf = new StringBuilder(); + + if (anonymous) { + + ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + + buf.append("("); + + if (!lambda && constructor != null) { + + InvocationExprent invsuper = child.superInvocation; + + ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(invsuper.getClassname()); + + List<VarVersionPaar> sigFields = null; + if (newnode != null) { // own class + if (newnode.wrapper != null) { + sigFields = newnode.wrapper.getMethodWrapper("<init>", invsuper.getStringDescriptor()).signatureFields; + } else { + if(newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && + !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance + sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(constructor.getLstParameters().size(), (VarVersionPaar)null)); + sigFields.set(0, new VarVersionPaar(-1, 0)); + } + } + } + + boolean firstpar = true; + int start = 0, end = invsuper.getLstParameters().size(); + if (enumconst) { + start += 2; + end -= 1; + } + for(int i = start; i < end; i++) { + if (sigFields == null || sigFields.get(i) == null) { + if (!firstpar) { + buf.append(", "); + } + + Exprent param = invsuper.getLstParameters().get(i); + if (param.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent) param).getIndex(); + if (varindex > 0 && varindex <= constructor.getLstParameters().size()) { + param = constructor.getLstParameters().get(varindex - 1); + } + } + + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(param, invsuper.getDescriptor().params[i], buff, indent, true); + + buf.append(buff); + firstpar = false; + } + } + + } + + if (!enumconst) { + String enclosing = null; + if (!lambda && constructor != null) { + enclosing = getQualifiedNewInstance(child.anonimousClassType.value, constructor.getLstParameters(), indent); + } + + String typename = ExprProcessor.getCastTypeName(child.anonimousClassType); + + if (enclosing != null) { + ClassNode anonimousNode = DecompilerContext.getClassprocessor().getMapRootClasses().get(child.anonimousClassType.value); + if (anonimousNode != null) { + typename = anonimousNode.simpleName; + } else { + typename = typename.substring(typename.lastIndexOf('.') + 1); + } + } + buf.insert(0, "new " + typename); + + if (enclosing != null) { + buf.insert(0, enclosing + "."); + } + } + + buf.append(")"); + + if (enumconst && buf.length() == 2) { + buf.setLength(0); + } + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + ClassWriter clwriter = new ClassWriter(); + try { + if (lambda) { + clwriter.classLambdaToJava(child, bufstrwriter, (constructor == null ? null : constructor.getInstance()), indent); + } else { + clwriter.classToJava(child, bufstrwriter, indent); + } + bufstrwriter.flush(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + if (lambda && !DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { + buf.setLength(0); // remove the usual 'new <class>()', it will + // be replaced with lambda style '() ->' + } + + buf.append(strwriter.toString()); + + } else if (directArrayInit) { + VarType leftType = newtype.copy(); + leftType.decArrayDim(); + + buf.append("{"); + for(int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); + } + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false); + + buf.append(buff); + } + buf.append("}"); + } else { + if (newtype.arraydim == 0) { + + if (constructor != null) { + + List<Exprent> lstParameters = constructor.getLstParameters(); + + ClassNode newnode = DecompilerContext.getClassprocessor().getMapRootClasses().get(constructor.getClassname()); + + List<VarVersionPaar> sigFields = null; + if (newnode != null) { // own class + if (newnode.wrapper != null) { + sigFields = newnode.wrapper.getMethodWrapper("<init>", constructor.getStringDescriptor()).signatureFields; + } else { + if (newnode.type == ClassNode.CLASS_MEMBER && (newnode.access & CodeConstants.ACC_STATIC) == 0 && + !constructor.getLstParameters().isEmpty()) { // member non-static class invoked with enclosing class instance + sigFields = new ArrayList<VarVersionPaar>(Collections.nCopies(lstParameters.size(), (VarVersionPaar) null)); + sigFields.set(0, new VarVersionPaar(-1, 0)); + } + } + } + + int start = enumconst ? 2 : 0; + if (!enumconst || start < lstParameters.size()) { + buf.append("("); + + boolean firstpar = true; + for(int i = start; i < lstParameters.size(); i++) { + if (sigFields == null || sigFields.get(i) == null) { + if (!firstpar) { + buf.append(", "); + } + + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstParameters.get(i), constructor.getDescriptor().params[i], buff, indent, true); + + buf.append(buff); + firstpar = false; + } + } + buf.append(")"); + } + } + + if (!enumconst) { + String enclosing = null; + if (constructor != null) { + enclosing = getQualifiedNewInstance(newtype.value, constructor.getLstParameters(), indent); + } + + String typename = ExprProcessor.getTypeName(newtype); + + if (enclosing != null) { + ClassNode newNode = DecompilerContext.getClassprocessor().getMapRootClasses().get(newtype.value); + if (newNode != null) { + typename = newNode.simpleName; + } else { + typename = typename.substring(typename.lastIndexOf('.') + 1); + } + } + buf.insert(0, "new " + typename); + + if (enclosing != null) { + buf.insert(0, enclosing + "."); + } + } + + } else { + buf.append("new ").append(ExprProcessor.getTypeName(newtype)); + + if (lstArrayElements.isEmpty()) { + for(int i = 0; i < newtype.arraydim; i++) { + buf.append("[").append(i < lstDims.size() ? lstDims.get(i).toJava(indent) : "").append("]"); + } + } else { + for(int i = 0; i < newtype.arraydim; i++) { + buf.append("[]"); + } + + VarType leftType = newtype.copy(); + leftType.decArrayDim(); + + buf.append("{"); + for(int i = 0; i < lstArrayElements.size(); i++) { + if (i > 0) { + buf.append(", "); + } + StringBuilder buff = new StringBuilder(); + ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buff, indent, false); + + buf.append(buff); + } + buf.append("}"); + } + } + } + return buf.toString(); + } + + private String getQualifiedNewInstance(String classname, List<Exprent> lstParams, int indent) { + + ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(classname); + + if(node != null && node.type != ClassNode.CLASS_ROOT && (node.access & CodeConstants.ACC_STATIC) == 0) { + if(!lstParams.isEmpty()) { + Exprent enclosing = lstParams.get(0); + + boolean isQualifiedNew = false; + + if(enclosing.type == Exprent.EXPRENT_VAR) { + VarExprent varEnclosing = (VarExprent)enclosing; + + StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASSNODE)).classStruct; + String this_classname = varEnclosing.getProcessor().getThisvars().get(new VarVersionPaar(varEnclosing)); + + if(!current_class.qualifiedName.equals(this_classname)) { + isQualifiedNew = true; + } + } else { + isQualifiedNew = true; + } + + if(isQualifiedNew) { + return enclosing.toJava(indent); + } + } + } + + return null; + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof NewExprent)) return false; + + NewExprent ne = (NewExprent)o; + return InterpreterUtil.equalObjects(newtype, ne.getNewtype()) && + InterpreterUtil.equalLists(lstDims, ne.getLstDims()) && + InterpreterUtil.equalObjects(constructor, ne.getConstructor()) && + directArrayInit == ne.directArrayInit && + InterpreterUtil.equalLists(lstArrayElements, ne.getLstArrayElements()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == constructor) { + constructor = (InvocationExprent)newexpr; + } + + if(constructor != null) { + constructor.replaceExprent(oldexpr, newexpr); + } + + for(int i=0;i<lstDims.size();i++) { + if(oldexpr == lstDims.get(i)) { + lstDims.set(i, newexpr); + } + } + + for(int i=0;i<lstArrayElements.size();i++) { + if(oldexpr == lstArrayElements.get(i)) { + lstArrayElements.set(i, newexpr); + } + } + } + + public InvocationExprent getConstructor() { + return constructor; + } + + public void setConstructor(InvocationExprent constructor) { + this.constructor = constructor; + } + + public List<Exprent> getLstDims() { + return lstDims; + } + + public VarType getNewtype() { + return newtype; + } + + public List<Exprent> getLstArrayElements() { + return lstArrayElements; + } + + public void setLstArrayElements(List<Exprent> lstArrayElements) { + this.lstArrayElements = lstArrayElements; + } + + public boolean isDirectArrayInit() { + return directArrayInit; + } + + public void setDirectArrayInit(boolean directArrayInit) { + this.directArrayInit = directArrayInit; + } + + public boolean isLambda() { + return lambda; + } + + public boolean isAnonymous() { + return anonymous; + } + + public void setAnonymous(boolean anonymous) { + this.anonymous = anonymous; + } + + public boolean isEnumconst() { + return enumconst; + } + + public void setEnumconst(boolean enumconst) { + this.enumconst = enumconst; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java new file mode 100644 index 0000000..13bd3b4 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java @@ -0,0 +1,113 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class SwitchExprent extends Exprent { + + private Exprent value; + + private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); + + { + this.type = EXPRENT_SWITCH; + } + + public SwitchExprent(Exprent value) { + this.value = value; + } + + public Exprent copy() { + SwitchExprent swexpr = new SwitchExprent(value.copy()); + + List<List<ConstExprent>> lstCaseValues = new ArrayList<List<ConstExprent>>(); + for(List<ConstExprent> lst: caseValues) { + lstCaseValues.add(new ArrayList<ConstExprent>(lst)); + } + swexpr.setCaseValues(lstCaseValues); + + return swexpr; + } + + public VarType getExprType() { + return value.getExprType(); + } + + public CheckTypesResult checkExprTypeBounds() { + CheckTypesResult result = new CheckTypesResult(); + + result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR); + result.addMaxTypeExprent(value, VarType.VARTYPE_INT); + + VarType valtype = value.getExprType(); + for(List<ConstExprent> lst: caseValues) { + for(ConstExprent expr: lst) { + if(expr != null) { + VarType casetype = expr.getExprType(); + if(!casetype.equals(valtype)) { + valtype = VarType.getCommonSupertype(casetype, valtype); + result.addMinTypeExprent(value, valtype); + } + } + } + } + + return result; + } + + public List<Exprent> getAllExprents() { + List<Exprent> lst = new ArrayList<Exprent>(); + lst.add(value); + return lst; + } + + public String toJava(int indent) { + return "switch("+value.toJava(indent)+")"; + } + + public boolean equals(Object o) { + if(o == this) { + return true; + } + + if(o == null || !(o instanceof SwitchExprent)) { + return false; + } + + SwitchExprent sw = (SwitchExprent) o; + return InterpreterUtil.equalObjects(value, sw.getValue()); + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(oldexpr == value) { + value = newexpr; + } + } + + public Exprent getValue() { + return value; + } + + public void setCaseValues(List<List<ConstExprent>> caseValues) { + this.caseValues = caseValues; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java new file mode 100644 index 0000000..c2356ca --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -0,0 +1,198 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.exps; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.ClassWriter; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class VarExprent extends Exprent { + + public static final int STACK_BASE = 10000; + + public static final String VAR_NAMELESS_ENCLOSURE = "<VAR_NAMELESS_ENCLOSURE>"; + + private int index; + + private VarType vartype; + + private boolean definition = false;; + + private VarProcessor processor; + + private int version = 0; + + private boolean classdef = false; + + private boolean stack = false;; + + { + this.type = EXPRENT_VAR; + } + + public VarExprent(int index, VarType vartype, VarProcessor processor) { + this.index = index; + this.vartype = vartype; + this.processor = processor; + } + + public VarType getExprType() { + return getVartype(); + } + + public int getExprentUse() { + return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; + } + + public List<Exprent> getAllExprents() { + return new ArrayList<Exprent>(); + } + + public Exprent copy() { + VarExprent var = new VarExprent(index, getVartype(), processor); + var.setDefinition(definition); + var.setVersion(version); + var.setClassdef(classdef); + var.setStack(stack); + return var; + } + + public String toJava(int indent) { + + if(classdef) { + + ClassNode child = DecompilerContext.getClassprocessor().getMapRootClasses().get(vartype.value); + + StringWriter strwriter = new StringWriter(); + BufferedWriter bufstrwriter = new BufferedWriter(strwriter); + + ClassWriter clwriter = new ClassWriter(); + try { + clwriter.classToJava(child, bufstrwriter, indent); + bufstrwriter.flush(); + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + return strwriter.toString(); + + } else { + String name = null; + if(processor != null) { + name = processor.getVarName(new VarVersionPaar(index, version)); + } + + StringBuilder buf = new StringBuilder(); + + if(definition) { + if(processor != null && processor.getVarFinal(new VarVersionPaar(index, version)) == VarTypeProcessor.VAR_FINALEXPLICIT) { + buf.append("final "); + } + buf.append(ExprProcessor.getCastTypeName(getVartype())+" "); + } + buf.append(name==null?("var"+index+(version==0?"":"_"+version)):name); + + return buf.toString(); + } + } + + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof VarExprent)) return false; + + VarExprent ve = (VarExprent)o; + return index == ve.getIndex() && + version == ve.getVersion() && + InterpreterUtil.equalObjects(getVartype(), ve.getVartype()); // FIXME: vartype comparison redundant? + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public VarType getVartype() { + VarType vt = null; + if(processor != null) { + vt = processor.getVarType(new VarVersionPaar(index, version)); + } + + if(vt == null || (vartype != null && vartype.type != CodeConstants.TYPE_UNKNOWN)) { + vt = vartype; + } + + return vt==null?VarType.VARTYPE_UNKNOWN:vt; + } + + public void setVartype(VarType vartype) { + this.vartype = vartype; + } + + public boolean isDefinition() { + return definition; + } + + public void setDefinition(boolean definition) { + this.definition = definition; + } + + public VarProcessor getProcessor() { + return processor; + } + + public void setProcessor(VarProcessor processor) { + this.processor = processor; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public boolean isClassdef() { + return classdef; + } + + public void setClassdef(boolean classdef) { + this.classdef = classdef; + } + + public boolean isStack() { + return stack; + } + + public void setStack(boolean stack) { + this.stack = stack; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java new file mode 100644 index 0000000..b35f801 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java @@ -0,0 +1,136 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.sforms; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + + +public class DirectGraph { + + public VBStyleCollection<DirectNode, String> nodes = new VBStyleCollection<DirectNode, String>(); + + public DirectNode first; + + // exit, [source, destination] + public HashMap<String, List<FinallyPathWrapper>> mapShortRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>(); + + // exit, [source, destination] + public HashMap<String, List<FinallyPathWrapper>> mapLongRangeFinallyPaths = new HashMap<String, List<FinallyPathWrapper>>(); + + // negative if branches (recorded for handling of && and ||) + public HashMap<String, String> mapNegIfBranch = new HashMap<String, String>(); + + // nodes, that are exception exits of a finally block with monitor variable + public HashMap<String, String> mapFinallyMonitorExceptionPathExits = new HashMap<String, String>(); + + public void sortReversePostOrder() { + LinkedList<DirectNode> res = new LinkedList<DirectNode>(); + addToReversePostOrderListIterative(first, res); + + nodes.clear(); + for(DirectNode node: res) { + nodes.addWithKey(node, node.id); + } + } + + private void addToReversePostOrderListIterative(DirectNode root, List<DirectNode> lst) { + + LinkedList<DirectNode> stackNode = new LinkedList<DirectNode>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + + stackNode.add(root); + stackIndex.add(0); + + while(!stackNode.isEmpty()) { + + DirectNode node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + for(;index<node.succs.size();index++) { + DirectNode succ = node.succs.get(index); + + if(!setVisited.contains(succ)) { + stackIndex.add(index+1); + + stackNode.add(succ); + stackIndex.add(0); + + break; + } + } + + if(index == node.succs.size()) { + lst.add(0, node); + + stackNode.removeLast(); + } + } + + } + + + public boolean iterateExprents(ExprentIterator iter) { + + LinkedList<DirectNode> stack = new LinkedList<DirectNode>(); + stack.add(first); + + HashSet<DirectNode> setVisited = new HashSet<DirectNode>(); + + while(!stack.isEmpty()) { + + DirectNode node = stack.removeFirst(); + + if(setVisited.contains(node)) { + continue; + } + setVisited.add(node); + + for(int i=0;i<node.exprents.size();i++) { + int res = iter.processExprent(node.exprents.get(i)); + + if(res == 1) { + return false; + } + + if(res == 2) { + node.exprents.remove(i); + i--; + } + } + + stack.addAll(node.succs); + } + + return true; + } + + public interface ExprentIterator { + // 0 - success, do nothing + // 1 - cancel iteration + // 2 - success, delete exprent + public int processExprent(Exprent exprent); + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java new file mode 100644 index 0000000..ebad3eb --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java @@ -0,0 +1,68 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.sforms; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; + + +public class DirectNode { + + public static final int NODE_DIRECT = 1; + public static final int NODE_TAIL = 2; + public static final int NODE_INIT = 3; + public static final int NODE_CONDITION = 4; + public static final int NODE_INCREMENT = 5; + public static final int NODE_TRY = 6; + + public int type; + + public String id; + + public BasicBlockStatement block; + + public Statement statement; + + public List<Exprent> exprents = new ArrayList<Exprent>(); + + public List<DirectNode> succs = new ArrayList<DirectNode>(); + + public List<DirectNode> preds = new ArrayList<DirectNode>(); + + public DirectNode(int type, Statement statement, String id) { + this.type = type; + this.statement = statement; + this.id = id; + } + + public DirectNode(int type, Statement statement, BasicBlockStatement block) { + this.type = type; + this.statement = statement; + + this.id = block.id.toString(); + this.block = block; + } + + @Override + public String toString() { + return id; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java new file mode 100644 index 0000000..9388073 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java @@ -0,0 +1,562 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.sforms; + +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 org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SwitchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; + + +public class FlattenStatementsHelper { + + // statement.id, node.id(direct), node.id(continue) + private HashMap<Integer, String[]> mapDestinationNodes = new HashMap<Integer, String[]>(); + + // node.id(source), statement.id(destination), edge type + private List<Edge> listEdges = new ArrayList<Edge>(); + + // node.id(exit), [node.id(source), statement.id(destination)] + public HashMap<String, List<String[]>> mapShortRangeFinallyPathIds = new HashMap<String, List<String[]>>(); + + // node.id(exit), [node.id(source), statement.id(destination)] + public HashMap<String, List<String[]>> mapLongRangeFinallyPathIds = new HashMap<String, List<String[]>>(); + + // positive if branches + public HashMap<String, String> mapPosIfBranch = new HashMap<String, String>(); + + + private DirectGraph graph; + + private RootStatement root; + + public DirectGraph buildDirectGraph(RootStatement root) { + + this.root = root; + + graph = new DirectGraph(); + + flattenStatement(); + + // dummy exit node + Statement dummyexit = root.getDummyExit(); + DirectNode node = new DirectNode(DirectNode.NODE_DIRECT, dummyexit, dummyexit.id.toString()); + node.exprents = new ArrayList<Exprent>(); + graph.nodes.addWithKey(node, node.id); + mapDestinationNodes.put(dummyexit.id, new String[] {node.id, null}); + + setEdges(); + + graph.first = graph.nodes.getWithKey(mapDestinationNodes.get(root.id)[0]); + graph.sortReversePostOrder(); + + return graph; + } + + private void flattenStatement() { + + class StatementStackEntry { + public Statement statement; + public LinkedList<StackEntry> stackFinally; + public List<Exprent> tailExprents; + + public int statementIndex; + public int edgeIndex; + public List<StatEdge> succEdges; + + public StatementStackEntry(Statement statement, LinkedList<StackEntry> stackFinally, List<Exprent> tailExprents) { + this.statement = statement; + this.stackFinally = stackFinally; + this.tailExprents = tailExprents; + } + } + + LinkedList<StatementStackEntry> lstStackStatements = new LinkedList<StatementStackEntry>(); + + lstStackStatements.add(new StatementStackEntry(root, new LinkedList<StackEntry>(), null)); + + mainloop: + while(!lstStackStatements.isEmpty()) { + + StatementStackEntry statEntry = lstStackStatements.removeFirst(); + + Statement stat = statEntry.statement; + LinkedList<StackEntry> stackFinally = statEntry.stackFinally; + int statementBreakIndex = statEntry.statementIndex; + + DirectNode node = null, nd = null; + + List<StatEdge> lstSuccEdges = new ArrayList<StatEdge>(); + DirectNode sourcenode = null; + + if(statEntry.succEdges == null) { + + switch(stat.type) { + case Statement.TYPE_BASICBLOCK: + node = new DirectNode(DirectNode.NODE_DIRECT, stat, (BasicBlockStatement)stat); + if(stat.getExprents() != null) { + node.exprents = stat.getExprents(); + } + graph.nodes.putWithKey(node, node.id); + mapDestinationNodes.put(stat.id, new String[] {node.id, null}); + + lstSuccEdges.addAll(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)); + sourcenode = node; + + List<Exprent> tailExprentList = statEntry.tailExprents; + + if(tailExprentList != null) { + DirectNode tail = new DirectNode(DirectNode.NODE_TAIL, stat, stat.id+"_tail"); + tail.exprents = tailExprentList; + graph.nodes.putWithKey(tail, tail.id); + + mapDestinationNodes.put(-stat.id, new String[] {tail.id, null}); + listEdges.add(new Edge(node.id, -stat.id, StatEdge.TYPE_REGULAR)); + + sourcenode = tail; + } + + // 'if' statement: record positive branch + if(stat.getLastBasicType() == Statement.LASTBASICTYPE_IF) { + mapPosIfBranch.put(sourcenode.id, lstSuccEdges.get(0).getDestination().id.toString()); + } + + break; + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + DirectNode firstnd = new DirectNode(DirectNode.NODE_TRY, stat, stat.id+"_try"); + + mapDestinationNodes.put(stat.id, new String[] {firstnd.id, null}); + graph.nodes.putWithKey(firstnd, firstnd.id); + + LinkedList<StatementStackEntry> lst = new LinkedList<StatementStackEntry>(); + + for(Statement st: stat.getStats()) { + listEdges.add(new Edge(firstnd.id, st.id, StatEdge.TYPE_REGULAR)); + + LinkedList<StackEntry> stack = stackFinally; + if(stat.type == Statement.TYPE_CATCHALL && ((CatchAllStatement)stat).isFinally()) { + stack = new LinkedList<StackEntry>(stackFinally); + + if(st == stat.getFirst()) { // catch head + stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE)); + } else { // handler + stack.add(new StackEntry((CatchAllStatement)stat, Boolean.TRUE, StatEdge.TYPE_BREAK, + root.getDummyExit(), st, st, firstnd, firstnd, true)); + } + } + lst.add(new StatementStackEntry(st, stack, null)); + } + + lstStackStatements.addAll(0, lst); + break; + case Statement.TYPE_DO: + if(statementBreakIndex == 0) { + statEntry.statementIndex = 1; + lstStackStatements.addFirst(statEntry); + lstStackStatements.addFirst(new StatementStackEntry(stat.getFirst(), stackFinally, null)); + + continue mainloop; + } + + nd = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]); + + DoStatement dostat = (DoStatement)stat; + int looptype = dostat.getLooptype(); + + if(looptype == DoStatement.LOOP_DO) { + mapDestinationNodes.put(stat.id, new String[] {nd.id, nd.id}); + break; + } + + lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge + + switch(looptype) { + case DoStatement.LOOP_WHILE: + case DoStatement.LOOP_DOWHILE: + node = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id+"_cond"); + node.exprents = dostat.getConditionExprentList(); + graph.nodes.putWithKey(node, node.id); + + listEdges.add(new Edge(node.id, stat.getFirst().id, StatEdge.TYPE_REGULAR)); + + if(looptype == DoStatement.LOOP_WHILE) { + mapDestinationNodes.put(stat.id, new String[] {node.id, node.id}); + } else { + mapDestinationNodes.put(stat.id, new String[] {nd.id, node.id}); + + boolean found = false; + for(Edge edge: listEdges) { + if(edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) { + found = true; + break; + } + } + if(!found) { + listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE)); + } + } + sourcenode = node; + break; + case DoStatement.LOOP_FOR: + DirectNode nodeinit = new DirectNode(DirectNode.NODE_INIT, stat, stat.id+"_init"); + if(dostat.getInitExprent() != null) { + nodeinit.exprents = dostat.getInitExprentList(); + } + graph.nodes.putWithKey(nodeinit, nodeinit.id); + + DirectNode nodecond = new DirectNode(DirectNode.NODE_CONDITION, stat, stat.id+"_cond"); + nodecond.exprents = dostat.getConditionExprentList(); + graph.nodes.putWithKey(nodecond, nodecond.id); + + DirectNode nodeinc = new DirectNode(DirectNode.NODE_INCREMENT, stat, stat.id+"_inc"); + nodeinc.exprents = dostat.getIncExprentList(); + graph.nodes.putWithKey(nodeinc, nodeinc.id); + + mapDestinationNodes.put(stat.id, new String[] {nodeinit.id, nodeinc.id}); + mapDestinationNodes.put(-stat.id, new String[] {nodecond.id, null}); + + listEdges.add(new Edge(nodecond.id, stat.getFirst().id, StatEdge.TYPE_REGULAR)); + listEdges.add(new Edge(nodeinit.id, -stat.id, StatEdge.TYPE_REGULAR)); + listEdges.add(new Edge(nodeinc.id, -stat.id, StatEdge.TYPE_REGULAR)); + + boolean found = false; + for(Edge edge: listEdges) { + if(edge.statid.equals(stat.id) && edge.edgetype == StatEdge.TYPE_CONTINUE) { + found = true; + break; + } + } + if(!found) { + listEdges.add(new Edge(nd.id, stat.id, StatEdge.TYPE_CONTINUE)); + } + + sourcenode = nodecond; + } + break; + case Statement.TYPE_SYNCRONIZED: + case Statement.TYPE_SWITCH: + case Statement.TYPE_IF: + case Statement.TYPE_SEQUENCE: + case Statement.TYPE_ROOT: + int statsize = stat.getStats().size(); + if(stat.type == Statement.TYPE_SYNCRONIZED) { + statsize = 2; // exclude the handler if synchronized + } + + if(statementBreakIndex <= statsize) { + List<Exprent> tailexprlst = null; + + switch(stat.type) { + case Statement.TYPE_SYNCRONIZED: + tailexprlst = ((SynchronizedStatement)stat).getHeadexprentList(); + break; + case Statement.TYPE_SWITCH: + tailexprlst = ((SwitchStatement)stat).getHeadexprentList(); + break; + case Statement.TYPE_IF: + tailexprlst = ((IfStatement)stat).getHeadexprentList(); + } + + for(int i=statementBreakIndex; i < statsize; i++) { + statEntry.statementIndex = i+1; + lstStackStatements.addFirst(statEntry); + lstStackStatements.addFirst( + new StatementStackEntry(stat.getStats().get(i), stackFinally, + (i==0 && tailexprlst != null && tailexprlst.get(0)!=null)?tailexprlst:null)); + + continue mainloop; + } + + node = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]); + mapDestinationNodes.put(stat.id, new String[] {node.id, null}); + + if(stat.type == Statement.TYPE_IF && ((IfStatement)stat).iftype == IfStatement.IFTYPE_IF) { + lstSuccEdges.add(stat.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0)); // exactly one edge + sourcenode = tailexprlst.get(0)==null?node:graph.nodes.getWithKey(node.id+"_tail"); + } + } + } + } + + // no successor edges + if(sourcenode != null) { + + if(statEntry.succEdges != null) { + lstSuccEdges = statEntry.succEdges; + } + + for(int edgeindex = statEntry.edgeIndex; edgeindex < lstSuccEdges.size(); edgeindex++) { + + StatEdge edge = lstSuccEdges.get(edgeindex); + + LinkedList<StackEntry> stack = new LinkedList<StackEntry>(stackFinally); + + int edgetype = edge.getType(); + Statement destination = edge.getDestination(); + + DirectNode finallyShortRangeSource = sourcenode; + DirectNode finallyLongRangeSource = sourcenode; + Statement finallyShortRangeEntry = null; + Statement finallyLongRangeEntry = null; + + boolean isFinallyMonitorExceptionPath = false; + + boolean isFinallyExit = false; + + for(;;) { + + StackEntry entry = null; + if(!stack.isEmpty()) { + entry = stack.getLast(); + } + + boolean created = true; + + if(entry == null) { + saveEdge(sourcenode, destination, edgetype, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + } else { + + CatchAllStatement catchall = entry.catchstatement; + + if(entry.state) { // finally handler statement + if(edgetype == StatEdge.TYPE_FINALLYEXIT) { + + stack.removeLast(); + destination = entry.destination; + edgetype = entry.edgetype; + + finallyShortRangeSource = entry.finallyShortRangeSource; + finallyLongRangeSource = entry.finallyLongRangeSource; + finallyShortRangeEntry = entry.finallyShortRangeEntry; + finallyLongRangeEntry = entry.finallyLongRangeEntry; + + isFinallyExit = true; + isFinallyMonitorExceptionPath = (catchall.getMonitor() != null) & entry.isFinallyExceptionPath; + + created = false; + } else { + if(!catchall.containsStatementStrict(destination)) { + stack.removeLast(); + created = false; + } else { + saveEdge(sourcenode, destination, edgetype, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + } + } + } else { // finally protected try statement + if(!catchall.containsStatementStrict(destination)) { + saveEdge(sourcenode, catchall.getHandler(), StatEdge.TYPE_REGULAR, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + + stack.removeLast(); + stack.add(new StackEntry(catchall, Boolean.TRUE, edgetype, destination, catchall.getHandler(), finallyLongRangeEntry==null?catchall.getHandler():finallyLongRangeEntry, + sourcenode, finallyLongRangeSource, false)); + + statEntry.edgeIndex = edgeindex+1; + statEntry.succEdges = lstSuccEdges; + lstStackStatements.addFirst(statEntry); + lstStackStatements.addFirst(new StatementStackEntry(catchall.getHandler(), stack, null)); + + continue mainloop; + } else { + saveEdge(sourcenode, destination, edgetype, isFinallyExit?finallyShortRangeSource:null, finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath); + } + } + } + + if(created) { + break; + } + } + } + } + + } + + } + + private void saveEdge(DirectNode sourcenode, Statement destination, int edgetype, DirectNode finallyShortRangeSource, + DirectNode finallyLongRangeSource, Statement finallyShortRangeEntry, Statement finallyLongRangeEntry, boolean isFinallyMonitorExceptionPath) { + + if(edgetype != StatEdge.TYPE_FINALLYEXIT) { + listEdges.add(new Edge(sourcenode.id, destination.id, edgetype)); + } + + if(finallyShortRangeSource != null) { + + boolean isContinueEdge = (edgetype == StatEdge.TYPE_CONTINUE); + + List<String[]> lst = mapShortRangeFinallyPathIds.get(sourcenode.id); + if(lst == null) { + mapShortRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>()); + } + lst.add(new String[]{finallyShortRangeSource.id, destination.id.toString(), finallyShortRangeEntry.id.toString(), isFinallyMonitorExceptionPath?"1":null, isContinueEdge?"1":null}); + + lst = mapLongRangeFinallyPathIds.get(sourcenode.id); + if(lst == null) { + mapLongRangeFinallyPathIds.put(sourcenode.id, lst = new ArrayList<String[]>()); + } + lst.add(new String[]{finallyLongRangeSource.id, destination.id.toString(), finallyLongRangeEntry.id.toString(), isContinueEdge?"1":null}); + } + + } + + private void setEdges() { + + for(Edge edge : listEdges) { + + String sourceid = edge.sourceid; + Integer statid = edge.statid; + + DirectNode source = graph.nodes.getWithKey(sourceid); + + DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(statid)[edge.edgetype==StatEdge.TYPE_CONTINUE?1:0]); + + if(!source.succs.contains(dest)) { + source.succs.add(dest); + } + + if(!dest.preds.contains(source)) { + dest.preds.add(source); + } + + if(mapPosIfBranch.containsKey(sourceid) && !statid.equals(mapPosIfBranch.get(sourceid))) { + graph.mapNegIfBranch.put(sourceid, dest.id); + } + } + + for(int i=0;i<2;i++) { + for(Entry<String, List<String[]>> ent : (i==0?mapShortRangeFinallyPathIds:mapLongRangeFinallyPathIds).entrySet()) { + + List<FinallyPathWrapper> newLst = new ArrayList<FinallyPathWrapper>(); + + List<String[]> lst = ent.getValue(); + for(String[] arr : lst) { + + boolean isContinueEdge = arr[i==0?4:3] != null; + + DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge?1:0]); + DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]); + + newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id)); + + if(i==0 && arr[3] != null) { + graph.mapFinallyMonitorExceptionPathExits.put(ent.getKey(), dest.id); + } + } + + if(!newLst.isEmpty()) { + (i==0?graph.mapShortRangeFinallyPaths:graph.mapLongRangeFinallyPaths).put(ent.getKey(), + new ArrayList<FinallyPathWrapper>(new HashSet<FinallyPathWrapper>(newLst))); + } + } + } + + } + + public HashMap<Integer, String[]> getMapDestinationNodes() { + return mapDestinationNodes; + } + + public static class FinallyPathWrapper { + public String source; + public String destination; + public String entry; + + private FinallyPathWrapper(String source, String destination, String entry) { + this.source = source; + this.destination = destination; + this.entry = entry; + } + + @Override + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof FinallyPathWrapper)) return false; + + FinallyPathWrapper fpw = (FinallyPathWrapper)o; + return (source+":"+destination+":"+entry).equals(fpw.source+":"+fpw.destination+":"+fpw.entry); + } + + @Override + public int hashCode() { + return (source+":"+destination+":"+entry).hashCode(); + } + + @Override + public String toString() { + return source + "->(" + entry + ")->" + destination; + } + } + + + private static class StackEntry { + + public CatchAllStatement catchstatement; + public boolean state; + public int edgetype; + public boolean isFinallyExceptionPath; + + public Statement destination; + public Statement finallyShortRangeEntry; + public Statement finallyLongRangeEntry; + public DirectNode finallyShortRangeSource; + public DirectNode finallyLongRangeSource; + + public StackEntry(CatchAllStatement catchstatement, boolean state, int edgetype, Statement destination, + Statement finallyShortRangeEntry, Statement finallyLongRangeEntry, DirectNode finallyShortRangeSource, DirectNode finallyLongRangeSource, boolean isFinallyExceptionPath) { + + this.catchstatement=catchstatement; + this.state=state; + this.edgetype=edgetype; + this.isFinallyExceptionPath = isFinallyExceptionPath; + + this.destination=destination; + this.finallyShortRangeEntry=finallyShortRangeEntry; + this.finallyLongRangeEntry=finallyLongRangeEntry; + this.finallyShortRangeSource=finallyShortRangeSource; + this.finallyLongRangeSource=finallyLongRangeSource; + } + + public StackEntry(CatchAllStatement catchstatement, boolean state) { + this(catchstatement, state, -1, null, null, null, null, null, false); + } + + } + + private static class Edge { + public String sourceid; + public Integer statid; + public int edgetype; + + public Edge(String sourceid, Integer statid, int edgetype) { + this.sourceid = sourceid; + this.statid = statid; + this.edgetype = edgetype; + } + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java new file mode 100644 index 0000000..e21f38a --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java @@ -0,0 +1,514 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.sforms; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; + +public class SSAConstructorSparseEx { + + // node id, var, version + private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>(); + + // node id, var, version (direct branch) + private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>(); + + // node id, var, version (negative branch) + private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>(); + + // node id, var, version + private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>(); + + // (var, version), version + private HashMap<VarVersionPaar, FastSparseSet<Integer>> phi = new HashMap<VarVersionPaar, FastSparseSet<Integer>>(); + + // var, version + private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>(); + + private List<VarVersionPaar> startVars = new ArrayList<VarVersionPaar>(); + + // set factory + private FastSparseSetFactory<Integer> factory; + + public void splitVariables(RootStatement root, StructMethod mt) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + + HashSet<Integer> setInit = new HashSet<Integer>(); + for(int i = 0; i < 64; i++) { + setInit.add(i); + } + factory = new FastSparseSetFactory<Integer>(setInit); + + SFormsFastMapDirect firstmap = createFirstMap(mt); + extraVarVersions.put(dgraph.first.id, firstmap); + + setCatchMaps(root, dgraph, flatthelper); + + HashSet<String> updated = new HashSet<String>(); + do { + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + ssaStatements(dgraph, updated); + // System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + } while (!updated.isEmpty()); + } + + private void ssaStatements(DirectGraph dgraph, HashSet<String> updated) { + + // try { + // DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); + // } catch(Exception ex) {ex.printStackTrace();} + + for(DirectNode node : dgraph.nodes) { + +// if (node.id.endsWith("_inc")) { +// System.out.println(); +// +// try { +// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr1_my.dot")); +// } catch (Exception ex) { +// ex.printStackTrace(); +// } +// } + + updated.remove(node.id); + mergeInVarMaps(node, dgraph); + + SFormsFastMapDirect varmap = inVarVersions.get(node.id); + varmap = new SFormsFastMapDirect(varmap); + + SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[] { varmap, null }; + + if (node.exprents != null) { + for(Exprent expr : node.exprents) { + processExprent(expr, varmaparr); + } + } + + if (varmaparr[1] == null) { + varmaparr[1] = varmaparr[0]; + } + + boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) + || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); + + if (this_updated) { + outVarVersions.put(node.id, varmaparr[0]); + if (dgraph.mapNegIfBranch.containsKey(node.id)) { + outNegVarVersions.put(node.id, varmaparr[1]); + } + + for(DirectNode nd : node.succs) { + updated.add(nd.id); + } + } + } + + } + + private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) { + + if (expr == null) { + return; + } + + VarExprent varassign = null; + boolean finished = false; + + switch (expr.type) { + case Exprent.EXPRENT_ASSIGNMENT: + AssignmentExprent assexpr = (AssignmentExprent) expr; + if (assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + Exprent dest = assexpr.getLeft(); + if (dest.type == Exprent.EXPRENT_VAR) { + varassign = (VarExprent) dest; + } + } + break; + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent) expr; + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_IIF: + processExprent(func.getLstOperands().get(0), varmaparr); + + SFormsFastMapDirect varmapFalse; + if (varmaparr[1] == null) { + varmapFalse = new SFormsFastMapDirect(varmaparr[0]); + } else { + varmapFalse = varmaparr[1]; + varmaparr[1] = null; + } + + processExprent(func.getLstOperands().get(1), varmaparr); + + SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] { varmapFalse, null }; + processExprent(func.getLstOperands().get(2), varmaparrNeg); + + mergeMaps(varmaparr[0], varmaparrNeg[0]); + varmaparr[1] = null; + + finished = true; + break; + case FunctionExprent.FUNCTION_CADD: + processExprent(func.getLstOperands().get(0), varmaparr); + + SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] { new SFormsFastMapDirect(varmaparr[0]), null }; + + processExprent(func.getLstOperands().get(1), varmaparrAnd); + + // false map + varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]); + // true map + varmaparr[0] = varmaparrAnd[0]; + + finished = true; + break; + case FunctionExprent.FUNCTION_COR: + processExprent(func.getLstOperands().get(0), varmaparr); + + SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] { new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null }; + + processExprent(func.getLstOperands().get(1), varmaparrOr); + + // false map + varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; + // true map + varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); + + finished = true; + } + } + + if (finished) { + return; + } + + List<Exprent> lst = expr.getAllExprents(); + lst.remove(varassign); + + for(Exprent ex : lst) { + processExprent(ex, varmaparr); + } + + SFormsFastMapDirect varmap = varmaparr[0]; + + if (varassign != null) { + + Integer varindex = varassign.getIndex(); + + if (varassign.getVersion() == 0) { + // get next version + Integer nextver = getNextFreeVersion(varindex); + + // set version + varassign.setVersion(nextver); + + setCurrentVar(varmap, varindex, nextver); + } else { + setCurrentVar(varmap, varindex, varassign.getVersion()); + } + + } else if (expr.type == Exprent.EXPRENT_VAR) { + + VarExprent vardest = (VarExprent) expr; + Integer varindex = vardest.getIndex(); + FastSparseSet<Integer> vers = varmap.get(varindex); + + int cardinality = vers.getCardinality(); + if (cardinality == 1) { // == 1 + // set version + Integer it = vers.iterator().next(); + vardest.setVersion(it.intValue()); + } else if (cardinality == 2) { // size > 1 + Integer current_vers = vardest.getVersion(); + + VarVersionPaar currpaar = new VarVersionPaar(varindex, current_vers); + if (current_vers != 0 && phi.containsKey(currpaar)) { + setCurrentVar(varmap, varindex, current_vers); + // update phi node + phi.get(currpaar).union(vers); + } else { + // increase version + Integer nextver = getNextFreeVersion(varindex); + // set version + vardest.setVersion(nextver); + + setCurrentVar(varmap, varindex, nextver); + // create new phi node + phi.put(new VarVersionPaar(varindex, nextver), vers); + + } + } // 0 means uninitialized variable, which is impossible + } + } + + private Integer getNextFreeVersion(Integer var) { + Integer nextver = lastversion.get(var); + if (nextver == null) { + nextver = new Integer(1); + } else { + nextver = new Integer(nextver.intValue() + 1); + } + lastversion.put(var, nextver); + return nextver; + } + + private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + for(DirectNode pred : node.preds) { + SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); + if (mapNew.isEmpty()) { + mapNew = mapOut.getCopy(); + } else { + mergeMaps(mapNew, mapOut); + } + } + + if (extraVarVersions.containsKey(node.id)) { + SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); + if (mapNew.isEmpty()) { + mapNew = mapExtra.getCopy(); + } else { + mergeMaps(mapNew, mapExtra); + } + } + + inVarVersions.put(node.id, mapNew); + } + + private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { + if (outNegVarVersions.containsKey(predid)) { + mapNew = outNegVarVersions.get(predid).getCopy(); + } + } else if (outVarVersions.containsKey(predid)) { + mapNew = outVarVersions.get(predid).getCopy(); + } + + boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); + + if (isFinallyExit && !mapNew.isEmpty()) { + + SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); + + SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); + + String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); + boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); + + HashSet<String> setLongPathWrapper = new HashSet<String>(); + for(FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) { + setLongPathWrapper.add(finwraplong.destination + "##" + finwraplong.source); + } + + for(FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { + SFormsFastMapDirect map; + + boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); + + if (recFinally) { + // recursion + map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); + } else { + if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { + map = outNegVarVersions.get(finwrap.source); + } else { + map = outVarVersions.get(finwrap.source); + } + } + + // false path? + boolean isFalsePath = true; + + if (recFinally) { + isFalsePath = !finwrap.destination.equals(nodeid); + } else { + isFalsePath = !setLongPathWrapper.contains(destid + "##" + finwrap.source); + } + + if (isFalsePath) { + mapNewTemp.complement(map); + } else { + if (mapTrueSource.isEmpty()) { + if (map != null) { + mapTrueSource = map.getCopy(); + } + } else { + mergeMaps(mapTrueSource, map); + } + } + } + + if (isExceptionMonitorExit) { + + mapNew = mapTrueSource; + + } else { + + mapNewTemp.union(mapTrueSource); + + SFormsFastMapDirect oldInMap = inVarVersions.get(nodeid); + if (oldInMap != null) { + mapNewTemp.union(oldInMap); + } + + mapNew.intersection(mapNewTemp); + } + } + + return mapNew; + } + + private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { + + if (map2 != null && !map2.isEmpty()) { + mapTo.union(map2); + } + + return mapTo; + } + + private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { + + if (map1 == null) { + return map2 == null; + } else if (map2 == null) { + return false; + } + + if (map1.size() != map2.size()) { + return false; + } + + for(Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) { + if (!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { + return false; + } + } + + return true; + } + + private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(vers); + varmap.put(var, set); + } + + private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { + + SFormsFastMapDirect map; + + switch (stat.type) { + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + + List<VarExprent> lstVars; + if (stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement) stat).getVars(); + } else { + lstVars = ((CatchStatement) stat).getVars(); + } + + for(int i = 1; i < stat.getStats().size(); i++) { + int varindex = lstVars.get(i - 1).getIndex(); + int version = getNextFreeVersion(varindex); // == 1 + + map = new SFormsFastMapDirect(); + setCurrentVar(map, varindex, version); + + extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); + startVars.add(new VarVersionPaar(varindex, version)); + } + } + + for(Statement st : stat.getStats()) { + setCatchMaps(st, dgraph, flatthelper); + } + } + + private SFormsFastMapDirect createFirstMap(StructMethod mt) { + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = md.params.length + (thisvar ? 1 : 0); + + int varindex = 0; + SFormsFastMapDirect map = new SFormsFastMapDirect(); + for(int i = 0; i < paramcount; i++) { + int version = getNextFreeVersion(varindex); // == 1 + + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(version); + map.put(varindex, set); + startVars.add(new VarVersionPaar(varindex, version)); + + if (thisvar) { + if (i == 0) { + varindex++; + } else { + varindex += md.params[i - 1].stack_size; + } + } else { + varindex += md.params[i].stack_size; + } + } + + return map; + } + + public HashMap<VarVersionPaar, FastSparseSet<Integer>> getPhi() { + return phi; + } + + public List<VarVersionPaar> getStartVars() { + return startVars; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java new file mode 100644 index 0000000..a59780f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java @@ -0,0 +1,833 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.sforms; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.SynchronizedStatement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory; +import org.jetbrains.java.decompiler.util.InterpreterUtil; +import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; +import org.jetbrains.java.decompiler.util.VBStyleCollection; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; + +public class SSAUConstructorSparseEx { + + // node id, var, version + private HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> inVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // node id, var, version (direct branch) + private HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // node id, var, version (negative branch) + private HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outNegVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // node id, var, version + private HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<String, SFormsFastMapDirect>(); + //private HashMap<String, HashMap<Integer, FastSet<Integer>>> extraVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>(); + + // (var, version), version + private HashMap<VarVersionPaar, HashSet<Integer>> phi = new HashMap<VarVersionPaar, HashSet<Integer>>(); + + // var, version + private HashMap<Integer, Integer> lastversion = new HashMap<Integer, Integer>(); + + // version, protected ranges (catch, finally) + private HashMap<VarVersionPaar, Integer> mapVersionFirstRange = new HashMap<VarVersionPaar, Integer>(); + + // version, version + private HashMap<VarVersionPaar, VarVersionPaar> phantomppnodes = new HashMap<VarVersionPaar, VarVersionPaar>(); // ++ and -- + + // node.id, version, version + private HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>> phantomexitnodes = new HashMap<String, HashMap<VarVersionPaar, VarVersionPaar>>(); // finally exits + + // versions memory dependencies + private VarVersionsGraph ssuversions = new VarVersionsGraph(); + + // field access vars (exprent id, var id) + private HashMap<Integer, Integer> mapFieldVars = new HashMap<Integer, Integer>(); + + // field access counter + private int fieldvarcounter = -1; + + // set factory + private FastSparseSetFactory<Integer> factory; + + public void splitVariables(RootStatement root, StructMethod mt) { + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + + HashSet<Integer> setInit = new HashSet<Integer>(); + for(int i=0;i<64;i++) { + setInit.add(i); + } + factory = new FastSparseSetFactory<Integer>(setInit); + + extraVarVersions.put(dgraph.first.id, createFirstMap(mt, root)); + + setCatchMaps(root, dgraph, flatthelper); + +// try { +// DotExporter.toDotFile(dgraph, new File("c:\\Temp\\gr12_my.dot")); +// } catch(Exception ex) {ex.printStackTrace();} + + HashSet<String> updated = new HashSet<String>(); + do { +// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + ssaStatements(dgraph, updated, false); +// System.out.println("~~~~~~~~~~~~~ \r\n"+root.toJava()); + } while(!updated.isEmpty()); + + + ssaStatements(dgraph, updated, true); + + ssuversions.initDominators(); + } + + private void ssaStatements(DirectGraph dgraph, HashSet<String> updated, boolean calcLiveVars) { + + for(DirectNode node: dgraph.nodes) { + + updated.remove(node.id); + mergeInVarMaps(node, dgraph); + + SFormsFastMapDirect varmap = new SFormsFastMapDirect(inVarVersions.get(node.id)); + + SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[] {varmap, null}; + + if(node.exprents != null) { + for(Exprent expr: node.exprents) { + processExprent(expr, varmaparr, node.statement, calcLiveVars); + } + } + + if(varmaparr[1] == null) { + varmaparr[1] = varmaparr[0]; + } + + // quick solution: 'dummy' field variables should not cross basic block borders (otherwise problems e.g. with finally loops - usage without assignment in a loop) + // For the full solution consider adding a dummy assignment at the entry point of the method + boolean allow_field_propagation = node.succs.isEmpty() || (node.succs.size() == 1 && node.succs.get(0).preds.size() == 1); + + if(!allow_field_propagation && varmaparr[0] != null) { + varmaparr[0].removeAllFields(); + varmaparr[1].removeAllFields(); + } + + boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id)) + || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id))); + + if(this_updated) { + + outVarVersions.put(node.id, varmaparr[0]); + if(dgraph.mapNegIfBranch.containsKey(node.id)) { + outNegVarVersions.put(node.id, varmaparr[1]); + } + + for(DirectNode nd: node.succs) { + updated.add(nd.id); + } + } + } + + } + + + private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr, Statement stat, boolean calcLiveVars) { + + if(expr == null) { + return; + } + + + VarExprent varassign = null; + boolean finished = false; + + switch(expr.type) { + case Exprent.EXPRENT_ASSIGNMENT: + AssignmentExprent assexpr = (AssignmentExprent)expr; + if(assexpr.getCondtype() == AssignmentExprent.CONDITION_NONE) { + Exprent dest = assexpr.getLeft(); + if(dest.type == Exprent.EXPRENT_VAR) { + varassign = (VarExprent)dest; + } + } + break; + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent)expr; + switch(func.getFunctype()) { + case FunctionExprent.FUNCTION_IIF: + processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect varmapFalse; + if(varmaparr[1] == null) { + varmapFalse = new SFormsFastMapDirect(varmaparr[0]); + } else { + varmapFalse = varmaparr[1]; + varmaparr[1] = null; + } + + processExprent(func.getLstOperands().get(1), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] {varmapFalse, null}; + processExprent(func.getLstOperands().get(2), varmaparrNeg, stat, calcLiveVars); + + mergeMaps(varmaparr[0], varmaparrNeg[0]); + varmaparr[1] = null; + + finished = true; + break; + case FunctionExprent.FUNCTION_CADD: + processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] {new SFormsFastMapDirect(varmaparr[0]), null}; + + processExprent(func.getLstOperands().get(1), varmaparrAnd, stat, calcLiveVars); + + // false map + varmaparr[1] = mergeMaps(varmaparr[varmaparr[1]==null?0:1], varmaparrAnd[varmaparrAnd[1]==null?0:1]); + // true map + varmaparr[0] = varmaparrAnd[0]; + + finished = true; + break; + case FunctionExprent.FUNCTION_COR: + processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars); + + SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] {new SFormsFastMapDirect(varmaparr[varmaparr[1]==null?0:1]), null}; + + processExprent(func.getLstOperands().get(1), varmaparrOr, stat, calcLiveVars); + + // false map + varmaparr[1] = varmaparrOr[varmaparrOr[1]==null?0:1]; + // true map + varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]); + + finished = true; + } + } + + if(!finished) { + List<Exprent> lst = expr.getAllExprents(); + lst.remove(varassign); + + for(Exprent ex: lst) { + processExprent(ex, varmaparr, stat, calcLiveVars); + } + } + + + SFormsFastMapDirect varmap = varmaparr[0]; + + // field access + if(expr.type == Exprent.EXPRENT_FIELD) { + + int index; + if(mapFieldVars.containsKey(expr.id)) { + index = mapFieldVars.get(expr.id); + } else { + index = fieldvarcounter--; + mapFieldVars.put(expr.id, index); + + // ssu graph + ssuversions.createNode(new VarVersionPaar(index, 1)); + } + + setCurrentVar(varmap, index, 1); + + } else if(expr.type == Exprent.EXPRENT_INVOCATION || + (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) || + (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewtype().type == CodeConstants.TYPE_OBJECT) || + expr.type == Exprent.EXPRENT_FUNCTION) { + + boolean ismmpp = true; + + if(expr.type == Exprent.EXPRENT_FUNCTION) { + + ismmpp = false; + + FunctionExprent fexpr = (FunctionExprent)expr; + if(fexpr.getFunctype() >= FunctionExprent.FUNCTION_IMM && fexpr.getFunctype() <= FunctionExprent.FUNCTION_PPI) { + if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) { + ismmpp = true; + } + } + } + + if(ismmpp) { + varmap.removeAllFields(); + } + } + + + if(varassign != null) { + + Integer varindex = varassign.getIndex(); + + if(varassign.getVersion() == 0) { + // get next version + Integer nextver = getNextFreeVersion(varindex, stat); + + // set version + varassign.setVersion(nextver); + + // ssu graph + ssuversions.createNode(new VarVersionPaar(varindex, nextver)); + + setCurrentVar(varmap, varindex, nextver); + } else { + if(calcLiveVars) { + varMapToGraph(new VarVersionPaar(varindex.intValue(), varassign.getVersion()), varmap); + } + setCurrentVar(varmap, varindex, varassign.getVersion()); + } + } else if(expr.type == Exprent.EXPRENT_FUNCTION) { // MM or PP function + FunctionExprent func = (FunctionExprent)expr; + + switch(func.getFunctype()) { + case FunctionExprent.FUNCTION_IMM: + case FunctionExprent.FUNCTION_MMI: + case FunctionExprent.FUNCTION_IPP: + case FunctionExprent.FUNCTION_PPI: + + if(func.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)func.getLstOperands().get(0); + Integer varindex = var.getIndex(); + VarVersionPaar varpaar = new VarVersionPaar(varindex.intValue(), var.getVersion()); + + // ssu graph + VarVersionPaar phantomver = phantomppnodes.get(varpaar); + if(phantomver == null) { + // get next version + Integer nextver = getNextFreeVersion(varindex, null); + phantomver = new VarVersionPaar(varindex, nextver); + //ssuversions.createOrGetNode(phantomver); + ssuversions.createNode(phantomver); + + VarVersionNode vernode = ssuversions.nodes.getWithKey(varpaar); + + FastSparseSet<Integer> vers = factory.spawnEmptySet(); + if(vernode.preds.size() == 1) { + vers.add(vernode.preds.iterator().next().source.version); + } else { + for(VarVersionEdge edge: vernode.preds) { + vers.add(edge.source.preds.iterator().next().source.version); + } + } + vers.add(nextver); + createOrUpdatePhiNode(varpaar, vers, stat); + phantomppnodes.put(varpaar, phantomver); + } + if(calcLiveVars) { + varMapToGraph(varpaar, varmap); + } + setCurrentVar(varmap, varindex.intValue(), var.getVersion()); + } + + } + } else if(expr.type == Exprent.EXPRENT_VAR) { + + VarExprent vardest = (VarExprent)expr; + + Integer varindex = vardest.getIndex(); + Integer current_vers = vardest.getVersion(); + + FastSparseSet<Integer> vers = varmap.get(varindex); + + int cardinality = vers.getCardinality(); + if(cardinality == 1) { // size == 1 + if(current_vers.intValue() != 0) { + if(calcLiveVars) { + varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); + } + setCurrentVar(varmap, varindex, current_vers); + } else { + // split last version + Integer usever = getNextFreeVersion(varindex, stat); + + // set version + vardest.setVersion(usever); + setCurrentVar(varmap, varindex, usever); + + // ssu graph + Integer lastver = vers.iterator().next(); + VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(varindex, lastver)); + VarVersionNode usenode = ssuversions.createNode(new VarVersionPaar(varindex, usever)); + VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, usenode); + prenode.addSuccessor(edge); + usenode.addPredecessor(edge); + } + } else if(cardinality == 2) { // size > 1 + + if(current_vers.intValue() != 0) { + if(calcLiveVars) { + varMapToGraph(new VarVersionPaar(varindex, current_vers), varmap); + } + setCurrentVar(varmap, varindex, current_vers); + } else { + // split version + Integer usever = getNextFreeVersion(varindex, stat); + // set version + vardest.setVersion(usever); + + // ssu node + ssuversions.createNode(new VarVersionPaar(varindex, usever)); + + setCurrentVar(varmap, varindex, usever); + + current_vers = usever; + } + + createOrUpdatePhiNode(new VarVersionPaar(varindex, current_vers), vers, stat); + + } // vers.size() == 0 means uninitialized variable, which is impossible + } + + + } + + private void createOrUpdatePhiNode(VarVersionPaar phivar, FastSparseSet<Integer> vers, Statement stat) { + + FastSparseSet<Integer> versCopy = vers.getCopy(); + HashSet<Integer> phiVers = new HashSet<Integer>(); + + // take into account the corresponding mm/pp node if existing + int ppvers = phantomppnodes.containsKey(phivar) ? phantomppnodes.get(phivar).version : -1; + + // ssu graph + VarVersionNode phinode = ssuversions.nodes.getWithKey(phivar); + List<VarVersionEdge> lstPreds = new ArrayList<VarVersionEdge>(phinode.preds); + if(lstPreds.size() == 1) { + // not yet a phi node + VarVersionEdge edge = lstPreds.get(0); + edge.source.removeSuccessor(edge); + phinode.removePredecessor(edge); + } else { + for(VarVersionEdge edge: lstPreds) { + int verssrc = edge.source.preds.iterator().next().source.version; + if(!vers.contains(verssrc) && verssrc != ppvers) { + edge.source.removeSuccessor(edge); + phinode.removePredecessor(edge); + } else { + versCopy.remove(verssrc); + phiVers.add(verssrc); + } + } + } + + List<VarVersionNode> colnodes = new ArrayList<VarVersionNode>(); + List<VarVersionPaar> colpaars = new ArrayList<VarVersionPaar>(); + + for(Integer ver: versCopy) { + + VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPaar(phivar.var, ver.intValue())); + + Integer tempver = getNextFreeVersion(phivar.var, stat); + + VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver.intValue()); + + colnodes.add(tempnode); + colpaars.add(new VarVersionPaar(phivar.var, tempver.intValue())); + + VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, tempnode); + + prenode.addSuccessor(edge); + tempnode.addPredecessor(edge); + + + edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, tempnode, phinode); + tempnode.addSuccessor(edge); + phinode.addPredecessor(edge); + + phiVers.add(tempver); + + } + + ssuversions.addNodes(colnodes, colpaars); + + // update phi node + phi.put(phivar, phiVers); + } + + private void varMapToGraph(VarVersionPaar varpaar, SFormsFastMapDirect varmap) { + + VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = ssuversions.nodes; + + VarVersionNode node = nodes.getWithKey(varpaar); + + node.live = new SFormsFastMapDirect(varmap); + } + + private Integer getNextFreeVersion(Integer var, Statement stat) { + + Integer nextver = lastversion.get(var); + + if(nextver==null) { + nextver = new Integer(1); + } else { + nextver = new Integer(nextver.intValue()+1); + } + lastversion.put(var, nextver); + + // save the first protected range, containing current statement + if(stat != null) { // null iff phantom version + Integer firstRangeId = getFirstProtectedRange(stat); + if(firstRangeId != null) { + mapVersionFirstRange.put(new VarVersionPaar(var, nextver), firstRangeId); + } + } + + return nextver; + } + + private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) { + + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + for(DirectNode pred: node.preds) { + SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id); + if(mapNew.isEmpty()) { + mapNew = mapOut.getCopy(); + } else { + mergeMaps(mapNew, mapOut); + } + } + + if(extraVarVersions.containsKey(node.id)) { + SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id); + if(mapNew.isEmpty()) { + mapNew = mapExtra.getCopy(); + } else { + mergeMaps(mapNew, mapExtra); + } + } + + inVarVersions.put(node.id, mapNew); + + } + + private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) { + + SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); + + boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid); + + if(nodeid.equals(dgraph.mapNegIfBranch.get(predid))) { + if(outNegVarVersions.containsKey(predid)) { + mapNew = outNegVarVersions.get(predid).getCopy(); + } + } else if(outVarVersions.containsKey(predid)) { + mapNew = outVarVersions.get(predid).getCopy(); + } + + if(isFinallyExit) { + + SFormsFastMapDirect mapNewTemp = mapNew.getCopy(); + + SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); + + String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid); + boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest)); + + HashSet<String> setLongPathWrapper = new HashSet<String>(); + for(List<FinallyPathWrapper> lstwrapper : dgraph.mapLongRangeFinallyPaths.values()) { + for(FinallyPathWrapper finwraplong : lstwrapper) { + setLongPathWrapper.add(finwraplong.destination+"##"+finwraplong.source); + } + } + + for(FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) { + SFormsFastMapDirect map; + + boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source); + + if(recFinally) { + // recursion + map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); + } else { + if(finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) { + map = outNegVarVersions.get(finwrap.source); + } else { + map = outVarVersions.get(finwrap.source); + } + } + + // false path? + boolean isFalsePath = true; + + if(recFinally) { + isFalsePath = !finwrap.destination.equals(nodeid); + } else { + isFalsePath = !setLongPathWrapper.contains(destid+"##"+finwrap.source); + } + + if(isFalsePath) { + mapNewTemp.complement(map); + } else { + if(mapTrueSource.isEmpty()) { + if(map != null) { + mapTrueSource = map.getCopy(); + } + } else { + mergeMaps(mapTrueSource, map); + } + } + + } + + if(isExceptionMonitorExit) { + + mapNew = mapTrueSource; + + } else { + + mapNewTemp.union(mapTrueSource); + mapNew.intersection(mapNewTemp); + + if(!mapTrueSource.isEmpty() && !mapNew.isEmpty()) { // FIXME: what for?? + + // replace phi versions with corresponding phantom ones + HashMap<VarVersionPaar, VarVersionPaar> mapPhantom = phantomexitnodes.get(predid); + if(mapPhantom == null) { + mapPhantom = new HashMap<VarVersionPaar, VarVersionPaar>(); + } + + SFormsFastMapDirect mapExitVar = mapNew.getCopy(); + mapExitVar.complement(mapTrueSource); + + for(Entry<Integer, FastSparseSet<Integer>> ent : mapExitVar.entryList()) { + for(Integer version : ent.getValue()) { + + Integer varindex = ent.getKey(); + VarVersionPaar exitvar = new VarVersionPaar(varindex, version); + FastSparseSet<Integer> newSet = mapNew.get(varindex); + + // remove the actual exit version + newSet.remove(version); + + // get or create phantom version + VarVersionPaar phantomvar = mapPhantom.get(exitvar); + if(phantomvar == null) { + Integer newversion = getNextFreeVersion(exitvar.var, null); + phantomvar = new VarVersionPaar(exitvar.var, newversion.intValue()); + + VarVersionNode exitnode = ssuversions.nodes.getWithKey(exitvar); + VarVersionNode phantomnode = ssuversions.createNode(phantomvar); + phantomnode.flags |= VarVersionNode.FLAG_PHANTOM_FINEXIT; + + VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_PHANTOM, exitnode, phantomnode); + exitnode.addSuccessor(edge); + phantomnode.addPredecessor(edge); + + mapPhantom.put(exitvar, phantomvar); + } + + // add phantom version + newSet.add(phantomvar.version); + } + } + + if(!mapPhantom.isEmpty()) { + phantomexitnodes.put(predid, mapPhantom); + } + } + } + + } + + return mapNew; + } + + private SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) { + + if(map2 != null && !map2.isEmpty()) { + mapTo.union(map2); + } + + return mapTo; + } + + private boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) { + + if(map1 == null) { + return map2 == null; + } else if (map2 == null) { + return false; + } + + if(map1.size() != map2.size()) { + return false; + } + + for(Entry<Integer, FastSparseSet<Integer>> ent2: map2.entryList()) { + if(!InterpreterUtil.equalObjects(map1.get(ent2.getKey()), ent2.getValue())) { + return false; + } + } + + return true; + } + + + private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) { + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(vers); + varmap.put(var, set); + } + + private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) { + + SFormsFastMapDirect map; + + switch(stat.type) { + case Statement.TYPE_CATCHALL: + case Statement.TYPE_TRYCATCH: + + List<VarExprent> lstVars; + if(stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)stat).getVars(); + } else { + lstVars = ((CatchStatement)stat).getVars(); + } + + for(int i=1;i<stat.getStats().size();i++) { + int varindex = lstVars.get(i-1).getIndex(); + int version = getNextFreeVersion(varindex, stat); // == 1 + + map = new SFormsFastMapDirect(); + setCurrentVar(map, varindex, version); + + extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map); + //ssuversions.createOrGetNode(new VarVersionPaar(varindex, version)); + ssuversions.createNode(new VarVersionPaar(varindex, version)); + } + } + + for(Statement st: stat.getStats()) { + setCatchMaps(st, dgraph, flatthelper); + } + } + + private SFormsFastMapDirect createFirstMap(StructMethod mt, RootStatement root) { + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = md.params.length + (thisvar?1:0); + + int varindex = 0; + SFormsFastMapDirect map = new SFormsFastMapDirect(); + for(int i=0;i<paramcount;i++) { + int version = getNextFreeVersion(varindex, root); // == 1 + + FastSparseSet<Integer> set = factory.spawnEmptySet(); + set.add(version); + map.put(varindex, set); + ssuversions.createNode(new VarVersionPaar(varindex, version)); + + if(thisvar) { + if(i==0) { + varindex++; + } else { + varindex+=md.params[i-1].stack_size; + } + } else { + varindex+=md.params[i].stack_size; + } + } + + return map; + } + + private Integer getFirstProtectedRange(Statement stat) { + + for(;;) { + Statement parent = stat.getParent(); + + if(parent == null) { + break; + } + + if(parent.type == Statement.TYPE_CATCHALL || + parent.type == Statement.TYPE_TRYCATCH) { + if(parent.getFirst() == stat) { + return parent.id; + } + } else if(parent.type == Statement.TYPE_SYNCRONIZED) { + if(((SynchronizedStatement)parent).getBody() == stat) { + return parent.id; + } + } + + stat = parent; + } + + return null; + } + + public HashMap<VarVersionPaar, HashSet<Integer>> getPhi() { + return phi; + } + + public VarVersionsGraph getSsuversions() { + return ssuversions; + } + + public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPaar varpaar) { + + + VarVersionNode node = ssuversions.nodes.getWithKey(varpaar); + if(node != null) { + return node.live; + } + + return null; + } + + public HashMap<VarVersionPaar, Integer> getMapVersionFirstRange() { + return mapVersionFirstRange; + } + + public HashMap<Integer, Integer> getMapFieldVars() { + return mapFieldVars; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java new file mode 100644 index 0000000..5cfa634 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java @@ -0,0 +1,95 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.code.SimpleInstructionSequence; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; + +public class BasicBlockStatement extends Statement { + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private BasicBlock block; + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public BasicBlockStatement(BasicBlock block) { + + type = Statement.TYPE_BASICBLOCK; + + this.block = block; + + id = block.id; + CounterContainer coun = DecompilerContext.getCountercontainer(); + if(id>=coun.getCounter(CounterContainer.STATEMENT_COUNTER)) { + coun.setCounter(CounterContainer.STATEMENT_COUNTER, id+1); + } + + Instruction instr = block.getLastInstruction(); + if(instr != null) { + if(instr.group==CodeConstants.GROUP_JUMP && instr.opcode != CodeConstants.opc_goto) { + lastBasicType = LASTBASICTYPE_IF; + } else if(instr.group==CodeConstants.GROUP_SWITCH) { + lastBasicType = LASTBASICTYPE_SWITCH; + } + } + + // monitorenter and monitorexits + buildMonitorFlags(); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public String toJava(int indent) { + return ExprProcessor.listToJava(varDefinitions, indent)+ + ExprProcessor.listToJava(exprents, indent); + } + + public Statement getSimpleCopy() { + + BasicBlock newblock = new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)); + + SimpleInstructionSequence seq = new SimpleInstructionSequence(); + for(int i=0;i<block.getSeq().length();i++) { + seq.addInstruction(block.getSeq().getInstr(i).clone(), -1); + } + + newblock.setSeq(seq); + + return new BasicBlockStatement(newblock); + } + + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public BasicBlock getBlock() { + return block; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java new file mode 100644 index 0000000..25adb29 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java @@ -0,0 +1,233 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class CatchAllStatement extends Statement { + + private Statement handler; + + private boolean isFinally; + + private VarExprent monitor; + + private List<VarExprent> vars = new ArrayList<VarExprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private CatchAllStatement(){ + type = Statement.TYPE_CATCHALL; + }; + + private CatchAllStatement(Statement head, Statement handler) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + this.handler = handler; + stats.addWithKey(handler, handler.id); + + List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstSuccs.isEmpty()) { + StatEdge edge = lstSuccs.get(0); + if(edge.getType() == StatEdge.TYPE_REGULAR) { + post = edge.getDestination(); + } + } + + vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if(head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { + return null; + } + + HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head); + + if(setHandlers.size() != 1) { + return null; + } + + for(StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + Statement exc = edge.getDestination(); + + if(edge.getExceptions() == null && setHandlers.contains(exc) && exc.getLastBasicType() == LASTBASICTYPE_GENERAL) { + List<StatEdge> lstSuccs = exc.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(lstSuccs.isEmpty() || lstSuccs.get(0).getType() != StatEdge.TYPE_REGULAR) { + + if(head.isMonitorEnter() || exc.isMonitorEnter()) { + return null; + } + + if(DecHelper.checkStatementExceptions(Arrays.asList(new Statement[] {head, exc}))) { + return new CatchAllStatement(head, exc); + } + } + } + } + + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + String indstr1 = null; + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + boolean labeled = isLabeled(); + if(labeled) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(first.type == TYPE_TRYCATCH && first.varDefinitions.isEmpty() && isFinally && + !labeled && !first.isLabeled() && (lstSuccs.isEmpty() || !lstSuccs.get(0).explicit)) { + String content = ExprProcessor.jmpWrapper(first, indent, true); + content = content.substring(0, content.length()-new_line_separator.length()); + + buf.append(content); + } else { + buf.append(indstr+"try {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); + buf.append(indstr+"}"); + } + + buf.append((isFinally?" finally": + " catch ("+vars.get(0).toJava(indent)+")")+" {" + new_line_separator); + + if(monitor != null) { + indstr1 = InterpreterUtil.getIndentString(indent+1); + buf.append(indstr1+"if("+monitor.toJava(indent)+") {" + new_line_separator); + } + + buf.append(ExprProcessor.jmpWrapper(handler, indent+1+(monitor != null?1:0), true)); + + if(monitor != null) { + buf.append(indstr1+"}" + new_line_separator); + } + + buf.append(indstr+"}" + new_line_separator); + + return buf.toString(); + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + if(handler == oldstat) { + handler = newstat; + } + + super.replaceStatement(oldstat, newstat); + } + + public Statement getSimpleCopy() { + + CatchAllStatement cas = new CatchAllStatement(); + + cas.isFinally = this.isFinally; + + if(this.monitor != null) { + cas.monitor = new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + VarType.VARTYPE_INT, + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR)); + } + + if(!this.vars.isEmpty()) { + // FIXME: WTF??? vars?! + vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Throwable"), + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + + return cas; + } + + public void initSimpleCopy() { + first = stats.get(0); + handler = stats.get(1); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Statement getHandler() { + return handler; + } + + + public void setHandler(Statement handler) { + this.handler = handler; + } + + + public boolean isFinally() { + return isFinally; + } + + + public void setFinally(boolean isFinally) { + this.isFinally = isFinally; + } + + + public VarExprent getMonitor() { + return monitor; + } + + + public void setMonitor(VarExprent monitor) { + this.monitor = monitor; + } + + public List<VarExprent> getVars() { + return vars; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java new file mode 100644 index 0000000..6387d45 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java @@ -0,0 +1,206 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class CatchStatement extends Statement { + + private List<List<String>> exctstrings = new ArrayList<List<String>>(); + + private List<VarExprent> vars = new ArrayList<VarExprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private CatchStatement() { + type = TYPE_TRYCATCH; + } + + private CatchStatement(Statement head, Statement next, HashSet<Statement> setHandlers) { + + this(); + + first = head; + stats.addWithKey(first, first.id); + + for(StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + Statement stat = edge.getDestination(); + + if(setHandlers.contains(stat)) { + stats.addWithKey(stat, stat.id); + exctstrings.add(new ArrayList<String>(edge.getExceptions())); + + vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)), // FIXME: for now simply the first type. Should get the first common superclass when possible. + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + } + + if(next != null) { + post = next; + } + + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if(head.getLastBasicType() != LASTBASICTYPE_GENERAL) { + return null; + } + + HashSet<Statement> setHandlers = DecHelper.getUniquePredExceptions(head); + + if(!setHandlers.isEmpty()) { + + int hnextcount = 0; // either no statements with connection to next, or more than 1 + + Statement next = null; + List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstHeadSuccs.isEmpty() && lstHeadSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) { + next = lstHeadSuccs.get(0).getDestination(); + hnextcount = 2; + } + + for(StatEdge edge : head.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) { + Statement stat = edge.getDestination(); + + boolean handlerok = true; + + if(edge.getExceptions() != null && setHandlers.contains(stat)) { + if(stat.getLastBasicType() != LASTBASICTYPE_GENERAL) { + handlerok = false; + } else { + List<StatEdge> lstStatSuccs = stat.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstStatSuccs.isEmpty() && lstStatSuccs.get(0).getType() == StatEdge.TYPE_REGULAR) { + + Statement statn = lstStatSuccs.get(0).getDestination(); + + if(next == null) { + next = statn; + } else if(next != statn) { + handlerok = false; + } + + if(handlerok) { + hnextcount++; + } + } + } + } else { + handlerok = false; + } + + if(!handlerok) { + setHandlers.remove(stat); + } + } + + if(hnextcount != 1 && !setHandlers.isEmpty()) { + List<Statement> lst = new ArrayList<Statement>(); + lst.add(head); + lst.addAll(setHandlers); + + for(Statement st : lst) { + if(st.isMonitorEnter()) { + return null; + } + } + + if(DecHelper.checkStatementExceptions(lst)) { + return new CatchStatement(head, next, setHandlers); + } + } + } + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + if(isLabeled()) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + buf.append(indstr+"try {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); + buf.append(indstr+"}"); + + for(int i=1;i<stats.size();i++) { + List<String> exception_types = exctstrings.get(i - 1); + + buf.append(" catch ("); + if(exception_types.size() > 1) { // multi-catch, Java 7 style + for(int exc_index = 1; exc_index < exception_types.size(); ++exc_index) { + VarType exc_type = new VarType(CodeConstants.TYPE_OBJECT, 0, exception_types.get(exc_index)); + String exc_type_name = ExprProcessor.getCastTypeName(exc_type); + + buf.append(exc_type_name + " | "); + } + } + buf.append(vars.get(i-1).toJava(indent)); + buf.append(") {"+new_line_separator+ExprProcessor.jmpWrapper(stats.get(i), indent+1, true)+indstr+"}"); + } + buf.append(new_line_separator); + + return buf.toString(); + } + + public Statement getSimpleCopy() { + + CatchStatement cs = new CatchStatement(); + + for(List<String> exc : this.exctstrings) { + cs.exctstrings.add(new ArrayList<String>(exc)); + cs.vars.add(new VarExprent(DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), + new VarType(CodeConstants.TYPE_OBJECT, 0, exc.get(0)), + (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR))); + } + + return cs; + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<VarExprent> getVars() { + return vars; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java new file mode 100644 index 0000000..604e36f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java @@ -0,0 +1,221 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class DoStatement extends Statement { + + public static final int LOOP_DO = 0; + public static final int LOOP_DOWHILE = 1; + public static final int LOOP_WHILE = 2; + public static final int LOOP_FOR = 3; + + private int looptype; + + private List<Exprent> initExprent = new ArrayList<Exprent>(); + private List<Exprent> conditionExprent = new ArrayList<Exprent>(); + private List<Exprent> incExprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private DoStatement() { + type = Statement.TYPE_DO; + looptype = LOOP_DO; + + initExprent.add(null); + conditionExprent.add(null); + incExprent.add(null); + } + + private DoStatement(Statement head) { + + this(); + + first = head; + stats.addWithKey(first, first.id); + + // post is always null! + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if(head.getLastBasicType() == LASTBASICTYPE_GENERAL && !head.isMonitorEnter()) { + + // at most one outgoing edge + StatEdge edge = null; + List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstSuccs.isEmpty()) { + edge = lstSuccs.get(0); + } + + // regular loop + if(edge!=null && edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() == head) { + return new DoStatement(head); + } + + // continues + if(head.type != TYPE_DO && (edge == null || edge.getType() != StatEdge.TYPE_REGULAR) && + head.getContinueSet().contains(head.getBasichead())) { + return new DoStatement(head); + } + } + + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + if(isLabeled()) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + switch(looptype) { + case LOOP_DO: + buf.append(indstr+"while(true) {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); + buf.append(indstr+"}" + new_line_separator); + break; + case LOOP_DOWHILE: + buf.append(indstr+"do {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); + buf.append(indstr+"} while("+conditionExprent.get(0).toJava(indent)+");" + new_line_separator); + break; + case LOOP_WHILE: + buf.append(indstr+"while("+conditionExprent.get(0).toJava(indent)+") {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); + buf.append(indstr+"}" + new_line_separator); + break; + case LOOP_FOR: + buf.append(indstr+"for("+(initExprent.get(0)==null?"":initExprent.get(0).toJava(indent))+ + "; "+conditionExprent.get(0).toJava(indent)+"; "+incExprent.get(0).toJava(indent)+") {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(first, indent+1, true)); + buf.append(indstr+"}" + new_line_separator); + } + + return buf.toString(); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(); + + switch(looptype) { + case LOOP_FOR: + if(getInitExprent() != null) { + lst.add(getInitExprent()); + } + case LOOP_WHILE: + lst.add(getConditionExprent()); + } + + lst.add(first); + + switch(looptype) { + case LOOP_DOWHILE: + lst.add(getConditionExprent()); + break; + case LOOP_FOR: + lst.add(getIncExprent()); + } + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(initExprent.get(0) == oldexpr) { + initExprent.set(0, newexpr); + } + if(conditionExprent.get(0) == oldexpr) { + conditionExprent.set(0, newexpr); + } + if(incExprent.get(0) == oldexpr) { + incExprent.set(0, newexpr); + } + } + + public Statement getSimpleCopy() { + return new DoStatement(); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public List<Exprent> getInitExprentList() { + return initExprent; + } + + public List<Exprent> getConditionExprentList() { + return conditionExprent; + } + + public List<Exprent> getIncExprentList() { + return incExprent; + } + + public Exprent getConditionExprent() { + return conditionExprent.get(0); + } + + public void setConditionExprent(Exprent conditionExprent) { + this.conditionExprent.set(0, conditionExprent); + } + + public Exprent getIncExprent() { + return incExprent.get(0); + } + + public void setIncExprent(Exprent incExprent) { + this.incExprent.set(0, incExprent); + } + + public Exprent getInitExprent() { + return initExprent.get(0); + } + + public void setInitExprent(Exprent initExprent) { + this.initExprent.set(0, initExprent); + } + + public int getLooptype() { + return looptype; + } + + public void setLooptype(int looptype) { + this.looptype = looptype; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java new file mode 100644 index 0000000..9cfaeb4 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java @@ -0,0 +1,75 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.Collection; +import java.util.HashSet; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + + +public class GeneralStatement extends Statement { + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private GeneralStatement() { + type = Statement.TYPE_GENERAL; + } + + public GeneralStatement(Statement head, Collection<Statement> statements, Statement post) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + HashSet<Statement> set = new HashSet<Statement>(statements); + set.remove(head); + + for(Statement st : set) { + stats.addWithKey(st, st.id); + } + + this.post = post; + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + if(isLabeled()) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + buf.append(indstr+"abstract statement {" + new_line_separator); + for(int i=0;i<stats.size();i++) { + buf.append(stats.get(i).toJava(indent+1)); + } + buf.append(indstr+"}"); + + return buf.toString(); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java new file mode 100644 index 0000000..3e3e20b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -0,0 +1,404 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class IfStatement extends Statement { + + public static int IFTYPE_IF = 0; + public static int IFTYPE_IFELSE = 1; + + public int iftype; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private Statement ifstat; + private Statement elsestat; + + private StatEdge ifedge; + private StatEdge elseedge; + + private boolean negated = false; + + private boolean iffflag; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); // contains IfExprent + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private IfStatement() { + type = TYPE_IF; + + headexprent.add(null); + } + + private IfStatement(Statement head, int regedges, Statement postst) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + + switch(regedges) { + case 0: + ifstat = null; + elsestat = null; + + break; + case 1: + ifstat = null; + elsestat = null; + + StatEdge edgeif = lstHeadSuccs.get(1); + if(edgeif.getType() != StatEdge.TYPE_REGULAR) { + post = lstHeadSuccs.get(0).getDestination(); + } else { + post = edgeif.getDestination(); + negated = true; + } + break; + case 2: + elsestat = lstHeadSuccs.get(0).getDestination(); + ifstat = lstHeadSuccs.get(1).getDestination(); + + List<StatEdge> lstSucc = ifstat.getSuccessorEdges(StatEdge.TYPE_REGULAR); + List<StatEdge> lstSucc1 = elsestat.getSuccessorEdges(StatEdge.TYPE_REGULAR); + + if(ifstat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size()>1 || lstSucc.size()>1) { + post = ifstat; + } else if(elsestat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size()>1 || lstSucc1.size()>1) { + post = elsestat; + } else { + if(lstSucc.size() == 0){ + post = elsestat; + } else if(lstSucc1.size() == 0){ + post = ifstat; + } + } + + if(ifstat == post) { + if(elsestat != post) { + ifstat = elsestat; + negated = true; + } else { + ifstat = null; + } + elsestat = null; + } else if(elsestat == post) { + elsestat = null; + } else { + post = postst; + } + + if(elsestat == null) { + regedges = 1; // if without else + } + } + + ifedge = lstHeadSuccs.get(negated?0:1); + elseedge = (regedges == 2)?lstHeadSuccs.get(negated?1:0):null; + + iftype = (regedges == 2)?IFTYPE_IFELSE:IFTYPE_IF; + + if(iftype == IFTYPE_IF) { + if(regedges == 0) { + StatEdge edge = lstHeadSuccs.get(0); + head.removeSuccessor(edge); + edge.setSource(this); + this.addSuccessor(edge); + } else if(regedges == 1) { + StatEdge edge = lstHeadSuccs.get(negated?1:0); + head.removeSuccessor(edge); + } + } + + if(ifstat != null) { + stats.addWithKey(ifstat, ifstat.id); + } + + if(elsestat != null) { + stats.addWithKey(elsestat, elsestat.id); + } + + if(post == head) { + post = this; + } + + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if(head.type == TYPE_BASICBLOCK && head.getLastBasicType() == LASTBASICTYPE_IF) { + int regsize = head.getSuccessorEdges(StatEdge.TYPE_REGULAR).size(); + + Statement p = null; + + boolean ok = (regsize < 2); + if(!ok) { + List<Statement> lst = new ArrayList<Statement>(); + if(DecHelper.isChoiceStatement(head, lst)) { + p = lst.remove(0); + + for(Statement st : lst) { + if(st.isMonitorEnter()) { + return null; + } + } + + ok = DecHelper.checkStatementExceptions(lst); + } + } + + if(ok) { + return new IfStatement(head, regsize, p); + } + } + + return null; + } + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + StringBuffer buf = new StringBuffer(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if(isLabeled()) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator); + + if(ifstat==null) { + buf.append(InterpreterUtil.getIndentString(indent+1)); + + if(ifedge.explicit) { + if(ifedge.getType() == StatEdge.TYPE_BREAK) { + // break + buf.append("break"); + } else { + // continue + buf.append("continue"); + } + + if(ifedge.labeled) { + buf.append(" label"+ifedge.closure.id); + } + } + buf.append(";" + new_line_separator); + } else { + buf.append(ExprProcessor.jmpWrapper(ifstat, indent+1, true)); + } + + boolean elseif = false; + + if(elsestat != null) { + if(elsestat.type == Statement.TYPE_IF + && elsestat.varDefinitions.isEmpty() && elsestat.getFirst().getExprents().isEmpty() && + !elsestat.isLabeled() && + (elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).isEmpty() + || !elsestat.getSuccessorEdges(STATEDGE_DIRECT_ALL).get(0).explicit)) { // else if + String content = ExprProcessor.jmpWrapper(elsestat, indent, false); + content = content.substring(indstr.length()); + + buf.append(indstr+"} else "); + buf.append(content); + + elseif = true; + } else { + String content = ExprProcessor.jmpWrapper(elsestat, indent+1, false); + + if(content.length() > 0) { + buf.append(indstr+"} else {" + new_line_separator); + buf.append(content); + } + } + } + + if(!elseif) { + buf.append(indstr+"}" + new_line_separator); + } + + return buf.toString(); + } + + public void initExprents() { + + IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size()-1); + + if(negated) { + ifexpr = (IfExprent)ifexpr.copy(); + ifexpr.negateIf(); + } + + headexprent.set(0, ifexpr); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + super.replaceStatement(oldstat, newstat); + + if(ifstat == oldstat) { + ifstat = newstat; + } + + if(elsestat == oldstat) { + elsestat = newstat; + } + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + + if(iftype == IFTYPE_IF) { + ifedge = lstSuccs.get(0); + elseedge = null; + } else { + StatEdge edge0 = lstSuccs.get(0); + StatEdge edge1 = lstSuccs.get(1); + if(edge0.getDestination() == ifstat) { + ifedge = edge0; + elseedge = edge1; + } else { + ifedge = edge1; + elseedge = edge0; + } + } + } + + public Statement getSimpleCopy() { + + IfStatement is = new IfStatement(); + is.iftype = this.iftype; + is.negated = this.negated; + is.iffflag = this.iffflag; + + return is; + } + + public void initSimpleCopy() { + + first = stats.get(0); + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + ifedge = lstSuccs.get((iftype == IFTYPE_IF || negated)?0:1); + if(stats.size() > 1) { + ifstat = stats.get(1); + } + + if(iftype == IFTYPE_IFELSE) { + elseedge = lstSuccs.get(negated?1:0); + elsestat = stats.get(2); + } + + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Statement getElsestat() { + return elsestat; + } + + public void setElsestat(Statement elsestat) { + this.elsestat = elsestat; + } + + public Statement getIfstat() { + return ifstat; + } + + public void setIfstat(Statement ifstat) { + this.ifstat = ifstat; + } + + public boolean isNegated() { + return negated; + } + + public void setNegated(boolean negated) { + this.negated = negated; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public IfExprent getHeadexprent() { + return (IfExprent)headexprent.get(0); + } + + public boolean isIffflag() { + return iffflag; + } + + public void setIffflag(boolean iffflag) { + this.iffflag = iffflag; + } + + public void setElseEdge(StatEdge elseedge) { + this.elseedge = elseedge; + } + + public void setIfEdge(StatEdge ifedge) { + this.ifedge = ifedge; + } + + public StatEdge getIfEdge() { + return ifedge; + } + + public StatEdge getElseEdge() { + return elseedge; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java new file mode 100644 index 0000000..c34888e --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java @@ -0,0 +1,49 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; + + +public class RootStatement extends Statement { + + private Statement dummyExit; + + public RootStatement(Statement head, Statement dummyExit) { + + type = Statement.TYPE_ROOT; + + first = head; + this.dummyExit = dummyExit; + + stats.addWithKey(first, first.id); + first.setParent(this); + + } + + public String toJava(int indent) { + return ExprProcessor.listToJava(varDefinitions, indent)+ + first.toJava(indent); + } + + public Statement getDummyExit() { + return dummyExit; + } + + public void setDummyExit(Statement dummyExit) { + this.dummyExit = dummyExit; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java new file mode 100644 index 0000000..79b0b19 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java @@ -0,0 +1,144 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.Arrays; +import java.util.List; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class SequenceStatement extends Statement { + + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private SequenceStatement() { + type = Statement.TYPE_SEQUENCE; + } + + public SequenceStatement(List<Statement> lst) { + + this(); + + lastBasicType = lst.get(lst.size()-1).getLastBasicType(); + + for(Statement st: lst) { + stats.addWithKey(st, st.id); + } + + first = stats.get(0); + } + + private SequenceStatement(Statement head, Statement tail) { + + this(Arrays.asList(new Statement[] {head, tail})); + + List<StatEdge> lstSuccs = tail.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstSuccs.isEmpty()) { + StatEdge edge = lstSuccs.get(0); + + if(edge.getType() == StatEdge.TYPE_REGULAR && edge.getDestination() != head) { + post = edge.getDestination(); + } + } + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead2Block(Statement head) { + + if(head.getLastBasicType() != Statement.LASTBASICTYPE_GENERAL) { + return null; + } + + // at most one outgoing edge + StatEdge edge = null; + List<StatEdge> lstSuccs = head.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstSuccs.isEmpty()) { + edge = lstSuccs.get(0); + } + + if(edge != null && edge.getType() == StatEdge.TYPE_REGULAR) { + Statement stat = edge.getDestination(); + + if(stat != head && stat.getPredecessorEdges(StatEdge.TYPE_REGULAR).size() == 1 + && !stat.isMonitorEnter()) { + + if(stat.getLastBasicType() == Statement.LASTBASICTYPE_GENERAL) { + if(DecHelper.checkStatementExceptions(Arrays.asList(new Statement[] {head, stat}))) { + return new SequenceStatement(head, stat); + } + } + } + } + + return null; + } + + public String toJava(int indent) { + + StringBuilder buf = new StringBuilder(); + + String indstr = null; + boolean islabeled = isLabeled(); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + + if(islabeled) { + indstr = InterpreterUtil.getIndentString(indent); + indent++; + buf.append(indstr+"label"+this.id+": {" + new_line_separator); + } + + boolean notempty = false; + + for(int i=0;i<stats.size();i++) { + + Statement st = stats.get(i); + + if(i>0 && notempty) { + buf.append(new_line_separator); + } + + String str = ExprProcessor.jmpWrapper(st, indent, false); + buf.append(str); + + notempty = (str.trim().length() > 0); + } + + if(islabeled) { + buf.append(indstr+"}" + new_line_separator); + } + + return buf.toString(); + } + + public Statement getSimpleCopy() { + return new SequenceStatement(); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java new file mode 100644 index 0000000..2f64035 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -0,0 +1,871 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.InstructionSequence; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +public class Statement { + + public static final int STATEDGE_ALL = 1 << 31; + public static final int STATEDGE_DIRECT_ALL = 1 << 30; + + public static final int DIRECTION_BACKWARD = 0; + public static final int DIRECTION_FORWARD = 1; + + public static final int TYPE_GENERAL = 0; + public static final int TYPE_IF = 2; + public static final int TYPE_DO = 5; + public static final int TYPE_SWITCH = 6; + public static final int TYPE_TRYCATCH = 7; + public static final int TYPE_BASICBLOCK = 8; + public static final int TYPE_FINALLY = 9; + public static final int TYPE_SYNCRONIZED = 10; + public static final int TYPE_PLACEHOLDER = 11; + public static final int TYPE_CATCHALL = 12; + public static final int TYPE_ROOT = 13; + public static final int TYPE_DUMMYEXIT = 14; + public static final int TYPE_SEQUENCE = 15; + + + public static final int LASTBASICTYPE_IF = 0; + public static final int LASTBASICTYPE_SWITCH = 1; + public static final int LASTBASICTYPE_GENERAL = 2; + + + // ***************************************************************************** + // public fields + // ***************************************************************************** + + public int type; + + public Integer id; + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private Map<Integer, List<StatEdge>> mapSuccEdges = new HashMap<Integer, List<StatEdge>>(); + private Map<Integer, List<StatEdge>> mapPredEdges = new HashMap<Integer, List<StatEdge>>(); + + private Map<Integer, List<Statement>> mapSuccStates = new HashMap<Integer, List<Statement>>(); + private Map<Integer, List<Statement>> mapPredStates = new HashMap<Integer, List<Statement>>(); + + // statement as graph + protected VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<Statement, Integer>(); + + protected Statement parent; + + protected Statement first; + + protected List<Exprent> exprents; + + protected HashSet<StatEdge> labelEdges = new HashSet<StatEdge>(); + + protected List<Exprent> varDefinitions = new ArrayList<Exprent>(); + + // copied statement, s. deobfuscating of irreducible CFGs + private boolean copied = false; + + // relevant for the first stage of processing only + // set to null after initializing of the statement structure + + protected Statement post; + + protected int lastBasicType = LASTBASICTYPE_GENERAL; + + protected boolean isMonitorEnter; + + protected boolean containsMonitorExit; + + protected HashSet<Statement> continueSet = new HashSet<Statement>(); + + // ***************************************************************************** + // initializers + // ***************************************************************************** + + { + // set statement id + id = DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER); + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public void clearTempInformation() { + + post = null; + continueSet = null; + + copied = false; + // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove + //lastBasicType = LASTBASICTYPE_GENERAL; + isMonitorEnter = false;; + containsMonitorExit = false; + + for(Map<Integer, List<StatEdge>> map : new Map[]{mapSuccEdges, mapPredEdges}) { + map.remove(StatEdge.TYPE_EXCEPTION); + + List<StatEdge> lst = map.get(STATEDGE_DIRECT_ALL); + if(lst != null) { + map.put(STATEDGE_ALL, new ArrayList<StatEdge>(lst)); + } else { + map.remove(STATEDGE_ALL); + } + } + + for(Map<Integer, List<Statement>> map : new Map[]{mapSuccStates, mapPredStates}) { + map.remove(StatEdge.TYPE_EXCEPTION); + + List<Statement> lst = map.get(STATEDGE_DIRECT_ALL); + if(lst != null) { + map.put(STATEDGE_ALL, new ArrayList<Statement>(lst)); + } else { + map.remove(STATEDGE_ALL); + } + } + + } + + public void collapseNodesToStatement(Statement stat) { + + Statement head = stat.getFirst(); + Statement post = stat.getPost(); + + VBStyleCollection<Statement, Integer> setNodes = stat.getStats(); + + // post edges + if(post != null) { + for(StatEdge edge : post.getEdges(STATEDGE_DIRECT_ALL, DIRECTION_BACKWARD)) { + if(stat.containsStatementStrict(edge.getSource())) { + edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK); + stat.addLabeledEdge(edge); + } + } + } + + // regular head edges + for(StatEdge prededge : head.getAllPredecessorEdges()) { + + if(prededge.getType() != StatEdge.TYPE_EXCEPTION && + stat.containsStatementStrict(prededge.getSource())) { + prededge.getSource().changeEdgeType(DIRECTION_FORWARD, prededge, StatEdge.TYPE_CONTINUE); + stat.addLabeledEdge(prededge); + } + + head.removePredecessor(prededge); + prededge.getSource().changeEdgeNode(DIRECTION_FORWARD, prededge, stat); + stat.addPredecessor(prededge); + } + + if(setNodes.containsKey(first.id)) { + first = stat; + } + + // exception edges + Set<Statement> setHandlers = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); + for(Statement node : setNodes) { + setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)); + } + + if(!setHandlers.isEmpty()) { + + for(StatEdge edge : head.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) { + Statement handler = edge.getDestination(); + + if(setHandlers.contains(handler)) { + if(!setNodes.containsKey(handler.id)) { + stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions())); + } + } + } + + for(Statement node : setNodes) { + for(StatEdge edge : node.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) { + if(setHandlers.contains(edge.getDestination())) { + node.removeSuccessor(edge); + } + } + } + } + + if(post!=null && !stat.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD).contains(post)) { // TODO: second condition redundant? + stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, post)); + } + + + // adjust statement collection + for(Statement st: setNodes) { + stats.removeWithKey(st.id); + } + + stats.addWithKey(stat, stat.id); + + stat.setAllParent(); + stat.setParent(this); + + stat.buildContinueSet(); + // monitorenter and monitorexit + stat.buildMonitorFlags(); + + if(stat.type == Statement.TYPE_SWITCH) { + // special case switch, sorting leaf nodes + ((SwitchStatement)stat).sortEdgesAndNodes(); + } + + } + + public void setAllParent() { + for(Statement st: stats) { + st.setParent(this); + } + } + + public void addLabeledEdge(StatEdge edge) { + + if(edge.closure != null) { + edge.closure.getLabelEdges().remove(edge); + } + edge.closure = this; + this.getLabelEdges().add(edge); + } + + private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { + + Map<Integer, List<StatEdge>> mapEdges = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; + Map<Integer, List<Statement>> mapStates = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; + + List<StatEdge> lst = mapEdges.get(edgetype); + if(lst == null) { + mapEdges.put(edgetype, lst = new ArrayList<StatEdge>()); + } + lst.add(edge); + + List<Statement> lstStates = mapStates.get(edgetype); + if(lstStates == null) { + mapStates.put(edgetype, lstStates = new ArrayList<Statement>()); + } + lstStates.add(direction==DIRECTION_BACKWARD?edge.getSource():edge.getDestination()); + } + + private void addEdgeInternal(int direction, StatEdge edge) { + + int type = edge.getType(); + + int[] arrtypes; + if(type == StatEdge.TYPE_EXCEPTION) { + arrtypes = new int[] {STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; + } else { + arrtypes = new int[] {STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; + } + + for(int edgetype : arrtypes) { + addEdgeDirectInternal(direction, edge, edgetype); + } + + } + + private void removeEdgeDirectInternal(int direction, StatEdge edge, int edgetype) { + + Map<Integer, List<StatEdge>> mapEdges = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; + Map<Integer, List<Statement>> mapStates = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; + + List<StatEdge> lst = mapEdges.get(edgetype); + if(lst != null) { + int index = lst.indexOf(edge); + if(index >= 0) { + lst.remove(index); + mapStates.get(edgetype).remove(index); + } + } + + } + + private void removeEdgeInternal(int direction, StatEdge edge) { + + int type = edge.getType(); + + int[] arrtypes; + if(type == StatEdge.TYPE_EXCEPTION) { + arrtypes = new int[] {STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; + } else { + arrtypes = new int[] {STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; + } + + for(int edgetype : arrtypes) { + removeEdgeDirectInternal(direction, edge, edgetype); + } + + } + + public void addPredecessor(StatEdge edge) { + addEdgeInternal(DIRECTION_BACKWARD, edge); + } + + public void removePredecessor(StatEdge edge) { + + if(edge == null) { // FIXME: redundant? + return; + } + + removeEdgeInternal(DIRECTION_BACKWARD, edge); + } + + public void addSuccessor(StatEdge edge) { + addEdgeInternal(DIRECTION_FORWARD, edge); + + if(edge.closure != null) { + edge.closure.getLabelEdges().add(edge); + } + + edge.getDestination().addPredecessor(edge); + } + + public void removeSuccessor(StatEdge edge) { + + if(edge == null) { + return; + } + + removeEdgeInternal(DIRECTION_FORWARD, edge); + + if(edge.closure != null) { + edge.closure.getLabelEdges().remove(edge); + } + + if(edge.getDestination() != null) { // TODO: redundant? + edge.getDestination().removePredecessor(edge); + } + } + + // TODO: make obsolete and remove + public void removeAllSuccessors(Statement stat) { + + if(stat == null) { + return; + } + + for(StatEdge edge : getAllSuccessorEdges()) { + if(edge.getDestination() == stat) { + removeSuccessor(edge); + } + } + } + + public HashSet<Statement> buildContinueSet() { + continueSet.clear(); + + for(Statement st: stats) { + continueSet.addAll(st.buildContinueSet()); + if(st != first) { + continueSet.remove(st.getBasichead()); + } + } + + for(StatEdge edge: getEdges(StatEdge.TYPE_CONTINUE, DIRECTION_FORWARD)) { + continueSet.add(edge.getDestination().getBasichead()); + } + + if(type == Statement.TYPE_DO) { + continueSet.remove(first.getBasichead()); + } + + return continueSet; + } + + public void buildMonitorFlags() { + + for(Statement st: stats) { + st.buildMonitorFlags(); + } + + switch(type) { + case TYPE_BASICBLOCK: + BasicBlockStatement bblock = (BasicBlockStatement)this; + InstructionSequence seq = bblock.getBlock().getSeq(); + + if(seq!=null && seq.length()>0) { + for(int i=0;i<seq.length();i++) { + if(seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) { + containsMonitorExit = true; + break; + } + } + isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter); + } + break; + case TYPE_SEQUENCE: + case TYPE_IF: + containsMonitorExit = false; + for(Statement st: stats) { + containsMonitorExit |= st.isContainsMonitorExit(); + } + + break; + case TYPE_SYNCRONIZED: + case TYPE_ROOT: + case TYPE_GENERAL: + break; + default: + containsMonitorExit = false; + for(Statement st: stats) { + containsMonitorExit |= st.isContainsMonitorExit(); + } + } + } + + + public List<Statement> getReversePostOrderList() { + return getReversePostOrderList(first); + } + + public List<Statement> getReversePostOrderList(Statement stat) { + List<Statement> res = new ArrayList<Statement>(); + + addToReversePostOrderListIterative(stat, res); + + return res; + } + + public List<Statement> getPostReversePostOrderList() { + return getPostReversePostOrderList(null); + } + + public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) { + + List<Statement> res = new ArrayList<Statement>(); + + if(lstexits == null) { + StrongConnectivityHelper schelper = new StrongConnectivityHelper(this); + lstexits = StrongConnectivityHelper.getExitReps(schelper.getComponents()); + } + + HashSet<Statement> setVisited = new HashSet<Statement>(); + + for(Statement exit : lstexits) { + addToPostReversePostOrderList(exit, res, setVisited); + } + + if(res.size() != stats.size()) { + DecompilerContext.getLogger().writeMessage("computing post reverse post order failed!", IFernflowerLogger.ERROR); + + throw new RuntimeException("parsing failure!"); + } + + return res; + } + + public boolean containsStatement(Statement stat) { + return this == stat || containsStatementStrict(stat); + } + + public boolean containsStatementStrict(Statement stat) { + + if(stats.contains(stat)) { + return true; + } + + for(int i=0;i<stats.size();i++) { + if(stats.get(i).containsStatementStrict(stat)) { + return true; + } + } + + return false; + } + + // to be overwritten + public String toJava() { + return toJava(0); + } + + public String toJava(int indent) { + throw new RuntimeException("not implemented"); + } + + // TODO: make obsolete and remove + public List<Object> getSequentialObjects() { + return new ArrayList<Object>(stats); + } + + public void initExprents() { + ; // do nothing + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + ; // do nothing + } + + public Statement getSimpleCopy() { + throw new RuntimeException("not implemented"); + } + + public void initSimpleCopy() { + if(!stats.isEmpty()) { + first = stats.get(0); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + for(StatEdge edge : oldstat.getAllPredecessorEdges()) { + oldstat.removePredecessor(edge); + edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat); + newstat.addPredecessor(edge); + } + + for(StatEdge edge : oldstat.getAllSuccessorEdges()) { + oldstat.removeSuccessor(edge); + edge.setSource(newstat); + newstat.addSuccessor(edge); + } + + int statindex = stats.getIndexByKey(oldstat.id); + stats.removeWithKey(oldstat.id); + stats.addWithKeyAndIndex(statindex, newstat, newstat.id); + + newstat.setParent(this); + newstat.post = oldstat.post; + + if(first == oldstat) { + first = newstat; + } + + List<StatEdge> lst = new ArrayList<StatEdge>(oldstat.getLabelEdges()); + + for(int i=lst.size()-1;i>=0;i--) { + StatEdge edge = lst.get(i); + if(edge.getSource() != newstat) { + newstat.addLabeledEdge(edge); + } else { + if(this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) { + edge.closure = null; + } else { + this.addLabeledEdge(edge); + } + } + } + + oldstat.getLabelEdges().clear(); + } + + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private void addToReversePostOrderListIterative(Statement root, List<Statement> lst) { + + LinkedList<Statement> stackNode = new LinkedList<Statement>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + HashSet<Statement> setVisited = new HashSet<Statement>(); + + stackNode.add(root); + stackIndex.add(0); + + while(!stackNode.isEmpty()) { + + Statement node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<StatEdge> lstEdges = node.getAllSuccessorEdges(); + + for(;index<lstEdges.size();index++) { + StatEdge edge = lstEdges.get(index); + Statement succ = edge.getDestination(); + + if(!setVisited.contains(succ) && + (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter? + + stackIndex.add(index+1); + + stackNode.add(succ); + stackIndex.add(0); + + break; + } + } + + if(index == lstEdges.size()) { + lst.add(0, node); + + stackNode.removeLast(); + } + } + + } + + + private void addToPostReversePostOrderList(Statement stat, List<Statement> lst, HashSet<Statement> setVisited) { + + if(setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible. + return; + } + setVisited.add(stat); + + for(StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) { + Statement pred = prededge.getSource(); + if(!setVisited.contains(pred)) { + addToPostReversePostOrderList(pred, lst, setVisited); + } + } + + lst.add(0, stat); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public void changeEdgeNode(int direction, StatEdge edge, Statement value) { + + Map<Integer, List<StatEdge>> mapEdges = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; + Map<Integer, List<Statement>> mapStates = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; + + int type = edge.getType(); + + int[] arrtypes; + if(type == StatEdge.TYPE_EXCEPTION) { + arrtypes = new int[] {STATEDGE_ALL, StatEdge.TYPE_EXCEPTION}; + } else { + arrtypes = new int[] {STATEDGE_ALL, STATEDGE_DIRECT_ALL, type}; + } + + for(int edgetype : arrtypes) { + List<StatEdge> lst = mapEdges.get(edgetype); + if(lst != null) { + int index = lst.indexOf(edge); + if(index >= 0) { + mapStates.get(edgetype).set(index, value); + } + } + } + + if(direction == DIRECTION_BACKWARD) { + edge.setSource(value); + } else { + edge.setDestination(value); + } + + } + + public void changeEdgeType(int direction, StatEdge edge, int newtype) { + + int oldtype = edge.getType(); + if(oldtype == newtype) { + return; + } + + if(oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) { + throw new RuntimeException("Invalid edge type!"); + } + + removeEdgeDirectInternal(direction, edge, oldtype); + addEdgeDirectInternal(direction, edge, newtype); + + if(direction == DIRECTION_FORWARD) { + edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype); + } + + edge.setType(newtype); + } + + + private List<StatEdge> getEdges(int type, int direction) { + + Map<Integer, List<StatEdge>> map = direction==DIRECTION_BACKWARD?mapPredEdges:mapSuccEdges; + + List<StatEdge> res; + if((type & (type -1)) == 0) { + res = map.get(type); + res = res==null?new ArrayList<StatEdge>():new ArrayList<StatEdge>(res); + } else { + res = new ArrayList<StatEdge>(); + for(int edgetype : StatEdge.TYPES) { + if((type & edgetype) != 0) { + List<StatEdge> lst = map.get(edgetype); + if(lst != null) { + res.addAll(lst); + } + } + } + } + + return res; + } + + public List<Statement> getNeighbours(int type, int direction) { + + Map<Integer, List<Statement>> map = direction==DIRECTION_BACKWARD?mapPredStates:mapSuccStates; + + List<Statement> res; + if((type & (type -1)) == 0) { + res = map.get(type); + res = res==null?new ArrayList<Statement>():new ArrayList<Statement>(res); + } else { + res = new ArrayList<Statement>(); + for(int edgetype : StatEdge.TYPES) { + if((type & edgetype) != 0) { + List<Statement> lst = map.get(edgetype); + if(lst != null) { + res.addAll(lst); + } + } + } + } + + return res; + } + + public Set<Statement> getNeighboursSet(int type, int direction) { + return new HashSet<Statement>(getNeighbours(type, direction)); + } + + public List<StatEdge> getSuccessorEdges(int type) { + return getEdges(type, DIRECTION_FORWARD); + } + + public List<StatEdge> getPredecessorEdges(int type) { + return getEdges(type, DIRECTION_BACKWARD); + } + + public List<StatEdge> getAllSuccessorEdges() { + return getEdges(STATEDGE_ALL, DIRECTION_FORWARD); + } + + public List<StatEdge> getAllPredecessorEdges() { + return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD); + } + + public Statement getFirst() { + return first; + } + + public void setFirst(Statement first) { + this.first = first; + } + + public Statement getPost() { + return post; + } + + public void setPost(Statement post) { + this.post = post; + } + + public VBStyleCollection<Statement, Integer> getStats() { + return stats; + } + + public int getLastBasicType() { + return lastBasicType; + } + + public HashSet<Statement> getContinueSet() { + return continueSet; + } + + public boolean isContainsMonitorExit() { + return containsMonitorExit; + } + + public boolean isMonitorEnter() { + return isMonitorEnter; + } + + public BasicBlockStatement getBasichead() { + if(type == Statement.TYPE_BASICBLOCK) { + return (BasicBlockStatement)this; + } else { + return first.getBasichead(); + } + } + + public boolean isLabeled() { + + for(StatEdge edge: labelEdges) { + if(edge.labeled && edge.explicit) { // FIXME: consistent setting + return true; + } + } + return false; + } + + public boolean hasBasicSuccEdge() { + boolean res = type == Statement.TYPE_BASICBLOCK || (type == Statement.TYPE_IF && + ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) || + (type == Statement.TYPE_DO && ((DoStatement)this).getLooptype() != DoStatement.LOOP_DO); + + // FIXME: default switch + + return res; + } + + + public Statement getParent() { + return parent; + } + + public void setParent(Statement parent) { + this.parent = parent; + } + + public HashSet<StatEdge> getLabelEdges() { // FIXME: why HashSet? + return labelEdges; + } + + public List<Exprent> getVarDefinitions() { + return varDefinitions; + } + + public List<Exprent> getExprents() { + return exprents; + } + + public void setExprents(List<Exprent> exprents) { + this.exprents = exprents; + } + + public boolean isCopied() { + return copied; + } + + public void setCopied(boolean copied) { + this.copied = copied; + } + + // helper methods + public String toString() { + return id.toString(); + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java new file mode 100644 index 0000000..ec03886 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java @@ -0,0 +1,370 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.code.SwitchInstruction; +import org.jetbrains.java.decompiler.code.cfg.BasicBlock; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.DecHelper; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +public class SwitchStatement extends Statement { + + // ***************************************************************************** + // private fields + // ***************************************************************************** + + private List<Statement> caseStatements = new ArrayList<Statement>(); + + private List<List<StatEdge>> caseEdges = new ArrayList<List<StatEdge>>(); + + private List<List<ConstExprent>> caseValues = new ArrayList<List<ConstExprent>>(); + + private StatEdge default_edge; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + private SwitchStatement() { + type = TYPE_SWITCH; + + headexprent.add(null); + } + + private SwitchStatement(Statement head, Statement poststat) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + // find post node + Set<Statement> lstNodes = new HashSet<Statement>(head.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_FORWARD)); + + // cluster nodes + if(poststat != null) { + post = poststat; + lstNodes.remove(post); + } + + default_edge = head.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); + + for(Statement st: lstNodes) { + stats.addWithKey(st, st.id); + } + + } + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public static Statement isHead(Statement head) { + + if(head.type == Statement.TYPE_BASICBLOCK && head.getLastBasicType() == Statement.LASTBASICTYPE_SWITCH) { + + List<Statement> lst = new ArrayList<Statement>(); + if(DecHelper.isChoiceStatement(head, lst)) { + Statement post = lst.remove(0); + + for(Statement st : lst) { + if(st.isMonitorEnter()) { + return null; + } + } + + if(DecHelper.checkStatementExceptions(lst)) { + return new SwitchStatement(head, post); + } + } + } + + return null; + } + + public String toJava(int indent) { + + String indstr = InterpreterUtil.getIndentString(indent); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuilder buf = new StringBuilder(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if(isLabeled()) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator); + + VarType switch_type = headexprent.get(0).getExprType(); + + for(int i=0;i<caseStatements.size();i++) { + + Statement stat = caseStatements.get(i); + List<StatEdge> edges = caseEdges.get(i); + List<ConstExprent> values = caseValues.get(i); + + for(int j=0;j<edges.size();j++) { + if(edges.get(j) == default_edge) { + buf.append(indstr+"default:" + new_line_separator); + } else { + ConstExprent value = (ConstExprent)values.get(j).copy(); + value.setConsttype(switch_type); + + buf.append(indstr+"case "+ value.toJava(indent)+":" + new_line_separator); + } + } + + buf.append(ExprProcessor.jmpWrapper(stat, indent+1, false)); + } + + buf.append(indstr+"}" + new_line_separator); + + return buf.toString(); + } + + public void initExprents() { + SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size()-1); + swexpr.setCaseValues(caseValues); + + headexprent.set(0, swexpr); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + for(int i=0;i<caseStatements.size();i++) { + if(caseStatements.get(i) == oldstat) { + caseStatements.set(i, newstat); + } + } + + super.replaceStatement(oldstat, newstat); + } + + public Statement getSimpleCopy() { + return new SwitchStatement(); + } + + public void initSimpleCopy() { + first = stats.get(0); + default_edge = first.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL).get(0); + + sortEdgesAndNodes(); + } + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + public void sortEdgesAndNodes() { + + HashMap<StatEdge, Integer> mapEdgeIndex = new HashMap<StatEdge, Integer>(); + + List<StatEdge> lstFirstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + for(int i=0;i<lstFirstSuccs.size();i++) { + mapEdgeIndex.put(lstFirstSuccs.get(i), i==0?lstFirstSuccs.size():i); + } + + // case values + BasicBlockStatement bbstat = (BasicBlockStatement)first; + int[] values = ((SwitchInstruction)bbstat.getBlock().getLastInstruction()).getValues(); + + List<Statement> nodes = new ArrayList<Statement>(); + List<List<Integer>> edges = new ArrayList<List<Integer>>(); + + // collect regular edges + for(int i=1;i<stats.size();i++) { + + Statement stat = stats.get(i); + + List<Integer> lst = new ArrayList<Integer>(); + for(StatEdge edge: stat.getPredecessorEdges(StatEdge.TYPE_REGULAR)) { + if(edge.getSource() == first) { + lst.add(mapEdgeIndex.get(edge)); + } + } + Collections.sort(lst); + + nodes.add(stat); + edges.add(lst); + } + + // collect exit edges + List<StatEdge> lstExitEdges = first.getSuccessorEdges(StatEdge.TYPE_BREAK | StatEdge.TYPE_CONTINUE); + while(!lstExitEdges.isEmpty()) { + StatEdge edge = lstExitEdges.get(0); + + List<Integer> lst = new ArrayList<Integer>(); + for(int i=lstExitEdges.size()-1;i>=0;i--) { + StatEdge edgeTemp = lstExitEdges.get(i); + if(edgeTemp.getDestination() == edge.getDestination() && edgeTemp.getType() == edge.getType()) { + lst.add(mapEdgeIndex.get(edgeTemp)); + lstExitEdges.remove(i); + } + } + Collections.sort(lst); + + nodes.add(null); + edges.add(lst); + } + + // sort edges (bubblesort) + for(int i=0;i<edges.size()-1;i++) { + for(int j=edges.size()-1;j>i;j--) { + if(edges.get(j-1).get(0) > edges.get(j).get(0)) { + edges.set(j, edges.set(j-1, edges.get(j))); + nodes.set(j, nodes.set(j-1, nodes.get(j))); + } + } + } + + // sort statement cliques + for(int index = 0; index < nodes.size(); index++) { + Statement stat = nodes.get(index); + + if(stat != null) { + HashSet<Statement> setPreds = new HashSet<Statement>(stat.getNeighbours(StatEdge.TYPE_REGULAR, DIRECTION_BACKWARD)); + setPreds.remove(first); + + if(!setPreds.isEmpty()) { + Statement pred = setPreds.iterator().next(); // assumption: at most one predecessor node besides the head. May not hold true for obfuscated code. + for(int j=0;j<nodes.size();j++) { + if(j != (index - 1) && nodes.get(j) == pred) { + nodes.add(j+1, stat); + edges.add(j+1, edges.get(index)); + + if(j > index) { + nodes.remove(index); + edges.remove(index); + index--; + } else { + nodes.remove(index + 1); + edges.remove(index + 1); + } + break; + } + } + } + } + } + + // translate indices back into edges + List<List<StatEdge>> lstEdges = new ArrayList<List<StatEdge>>(); + List<List<ConstExprent>> lstValues = new ArrayList<List<ConstExprent>>(); + + for(List<Integer> lst: edges) { + List<StatEdge> lste = new ArrayList<StatEdge>(); + List<ConstExprent> lstv = new ArrayList<ConstExprent>(); + + List<StatEdge> lstSuccs = first.getSuccessorEdges(STATEDGE_DIRECT_ALL); + for(Integer in: lst) { + int index = in==lstSuccs.size()?0:in; + + lste.add(lstSuccs.get(index)); + lstv.add(index==0?null:new ConstExprent(values[index-1], false)); + } + lstEdges.add(lste); + lstValues.add(lstv); + } + + // replace null statements with dummy basic blocks + for(int i=0;i<nodes.size();i++) { + if(nodes.get(i) == null) { + BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock( + DecompilerContext.getCountercontainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER))); + + StatEdge sample_edge = lstEdges.get(i).get(0); + + bstat.addSuccessor(new StatEdge(sample_edge.getType(), bstat, sample_edge.getDestination(), sample_edge.closure)); + + for(StatEdge edge : lstEdges.get(i)) { + + edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_REGULAR); + edge.closure.getLabelEdges().remove(edge); + + edge.getDestination().removePredecessor(edge); + edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, bstat); + bstat.addPredecessor(edge); + } + + nodes.set(i, bstat); + stats.addWithKey(bstat, bstat.id); + bstat.setParent(this); + } + } + + caseStatements = nodes; + caseEdges = lstEdges; + caseValues = lstValues; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public Exprent getHeadexprent() { + return headexprent.get(0); + } + + public List<List<StatEdge>> getCaseEdges() { + return caseEdges; + } + + public List<Statement> getCaseStatements() { + return caseStatements; + } + + public StatEdge getDefault_edge() { + return default_edge; + } + + public List<List<ConstExprent>> getCaseValues() { + return caseValues; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java new file mode 100644 index 0000000..5336f12 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java @@ -0,0 +1,154 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.stats; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper; +import org.jetbrains.java.decompiler.modules.decompiler.StatEdge; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + + +public class SynchronizedStatement extends Statement { + + private Statement body; + + private List<Exprent> headexprent = new ArrayList<Exprent>(); + + // ***************************************************************************** + // constructors + // ***************************************************************************** + + public SynchronizedStatement() { + type = TYPE_SYNCRONIZED; + + headexprent.add(null); + } + + public SynchronizedStatement(Statement head, Statement body, Statement exc) { + + this(); + + first = head; + stats.addWithKey(head, head.id); + + this.body = body; + stats.addWithKey(body, body.id); + + stats.addWithKey(exc, exc.id); + + List<StatEdge> lstSuccs = body.getSuccessorEdges(STATEDGE_DIRECT_ALL); + if(!lstSuccs.isEmpty()) { + StatEdge edge = lstSuccs.get(0); + if(edge.getType() == StatEdge.TYPE_REGULAR) { + post = edge.getDestination(); + } + } + + } + + + // ***************************************************************************** + // public methods + // ***************************************************************************** + + public String toJava(int indent) { + String indstr = InterpreterUtil.getIndentString(indent); + + String new_line_separator = DecompilerContext.getNewLineSeparator(); + + StringBuffer buf = new StringBuffer(); + buf.append(ExprProcessor.listToJava(varDefinitions, indent)); + buf.append(first.toJava(indent)); + + if(isLabeled()) { + buf.append(indstr+"label"+this.id+":" + new_line_separator); + } + + buf.append(indstr+headexprent.get(0).toJava(indent)+" {" + new_line_separator); + buf.append(ExprProcessor.jmpWrapper(body, indent+1, true)); + buf.append(indstr+"}" + new_line_separator); + + return buf.toString(); + } + + public void initExprents() { + headexprent.set(0, first.getExprents().remove(first.getExprents().size()-1)); + } + + public List<Object> getSequentialObjects() { + + List<Object> lst = new ArrayList<Object>(stats); + lst.add(1, headexprent.get(0)); + + return lst; + } + + public void replaceExprent(Exprent oldexpr, Exprent newexpr) { + if(headexprent.get(0) == oldexpr) { + headexprent.set(0, newexpr); + } + } + + public void replaceStatement(Statement oldstat, Statement newstat) { + + if(body == oldstat) { + body = newstat; + } + + super.replaceStatement(oldstat, newstat); + } + + public void removeExc() { + Statement exc = stats.get(2); + SequenceHelper.destroyStatementContent(exc, true); + + stats.removeWithKey(exc.id); + } + + public Statement getSimpleCopy() { + return new SynchronizedStatement(); + } + + public void initSimpleCopy() { + first = stats.get(0); + body = stats.get(1); + } + + // ***************************************************************************** + // getter and setter methods + // ***************************************************************************** + + public Statement getBody() { + return body; + } + + public void setBody(Statement body) { + this.body = body; + } + + public List<Exprent> getHeadexprentList() { + return headexprent; + } + + public Exprent getHeadexprent() { + return headexprent.get(0); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java new file mode 100644 index 0000000..5519797 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java @@ -0,0 +1,57 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class CheckTypesResult { + + private List<ExprentTypePair> lstMaxTypeExprents = new ArrayList<ExprentTypePair>(); + + private List<ExprentTypePair> lstMinTypeExprents = new ArrayList<ExprentTypePair>(); + + public void addMaxTypeExprent(Exprent exprent, VarType type) { + lstMaxTypeExprents.add(new ExprentTypePair(exprent, type, null)); + } + + public void addMinTypeExprent(Exprent exprent, VarType type) { + lstMinTypeExprents.add(new ExprentTypePair(exprent, type, null)); + } + + public List<ExprentTypePair> getLstMaxTypeExprents() { + return lstMaxTypeExprents; + } + + public List<ExprentTypePair> getLstMinTypeExprents() { + return lstMinTypeExprents; + } + + public class ExprentTypePair { + public Exprent exprent; + public VarType type; + public VarType desttype; + + public ExprentTypePair(Exprent exprent, VarType type, VarType desttype) { + this.exprent = exprent; + this.type = type; + this.desttype = desttype; + } + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java new file mode 100644 index 0000000..5c04ccd --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -0,0 +1,358 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; + +public class VarDefinitionHelper { + + private HashMap<Integer, Statement> mapVarDefStatements; + + // statement.id, defined vars + private HashMap<Integer, HashSet<Integer>> mapStatementVars; + + private HashSet<Integer> implDefVars; + + private VarProcessor varproc; + + public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) { + + mapVarDefStatements = new HashMap<Integer, Statement>(); + mapStatementVars = new HashMap<Integer, HashSet<Integer>>(); + implDefVars = new HashSet<Integer>(); + + this.varproc = varproc; + + VarNamesCollector vc = DecompilerContext.getVarncollector(); + + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + int paramcount = 0; + if(thisvar) { + paramcount = 1; + } + paramcount += md.params.length; + + + // method parameters are implicitly defined + int varindex = 0; + for(int i=0;i<paramcount;i++) { + implDefVars.add(varindex); + varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex)); + + if(thisvar) { + if(i==0) { + varindex++; + } else { + varindex+=md.params[i-1].stack_size; + } + } else { + varindex+=md.params[i].stack_size; + } + } + + if(thisvar) { + StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS); + + varproc.getThisvars().put(new VarVersionPaar(0, 0), current_class.qualifiedName); + varproc.setVarName(new VarVersionPaar(0, 0), "this"); + vc.addName("this"); + } + + // catch variables are implicitly defined + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while(!stack.isEmpty()) { + Statement st = stack.removeFirst(); + + List<VarExprent> lstVars = null; + if(st.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)st).getVars(); + } else if(st.type == Statement.TYPE_TRYCATCH) { + lstVars = ((CatchStatement)st).getVars(); + } + + if(lstVars != null) { + for(VarExprent var: lstVars) { + implDefVars.add(var.getIndex()); + varproc.setVarName(new VarVersionPaar(var), vc.getFreeName(var.getIndex())); + var.setDefinition(true); + } + } + + stack.addAll(st.getStats()); + } + + initStatement(root); + } + + + public void setVarDefinitions() { + + VarNamesCollector vc = DecompilerContext.getVarncollector(); + + Iterator<Entry<Integer, Statement>> it = mapVarDefStatements.entrySet().iterator(); + while(it.hasNext()) { + Entry<Integer, Statement> en = it.next(); + + Statement stat = en.getValue(); + Integer index = en.getKey(); + + if(implDefVars.contains(index)) { + // already implicitly defined + continue; + } + + varproc.setVarName(new VarVersionPaar(index.intValue(), 0), vc.getFreeName(index)); + + // special case for + if(stat.type == Statement.TYPE_DO) { + DoStatement dstat = (DoStatement)stat; + if(dstat.getLooptype() == DoStatement.LOOP_FOR) { + + if(dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) { + continue; + } else { + List<Exprent> lstSpecial = Arrays.asList(new Exprent[]{dstat.getConditionExprent(), dstat.getIncExprent()}); + for(VarExprent var: getAllVars(lstSpecial)) { + if(var.getIndex() == index.intValue()) { + stat = stat.getParent(); + break; + } + } + } + } + } + + + Statement first = findFirstBlock(stat, index); + + List<Exprent> lst; + if(first == null) { + lst = stat.getVarDefinitions(); + } else if(first.getExprents() == null) { + lst = first.getVarDefinitions(); + } else { + lst = first.getExprents(); + } + + + boolean defset = false; + + // search for the first assignement to var [index] + int addindex = 0; + for(Exprent expr: lst) { + if(setDefinition(expr, index)) { + defset = true; + break; + } else { + boolean foundvar = false; + for(Exprent exp: expr.getAllExprents(true)) { + if(exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) { + foundvar = true; + break; + } + } + if(foundvar) { + break; + } + } + addindex++; + } + + if(!defset) { + VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPaar(index.intValue(), 0)), varproc); + var.setDefinition(true); + + lst.add(addindex, var); + } + + } + + } + + + // ***************************************************************************** + // private methods + // ***************************************************************************** + + private Statement findFirstBlock(Statement stat, Integer varindex) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(stat); + + while(!stack.isEmpty()) { + Statement st = stack.remove(0); + + if(stack.isEmpty() || mapStatementVars.get(st.id).contains(varindex)) { + + if(st.isLabeled() && !stack.isEmpty()) { + return st; + } + + if(st.getExprents() != null) { + return st; + } else { + stack.clear(); + + switch(st.type) { + case Statement.TYPE_SEQUENCE: + stack.addAll(0, st.getStats()); + break; + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + stack.add(st.getFirst()); + break; + default: + return st; + } + } + } + } + + return null; + } + + private Set<Integer> initStatement(Statement stat) { + + HashMap<Integer, Integer> mapCount = new HashMap<Integer, Integer>(); + + List<VarExprent> condlst; + + if(stat.getExprents() == null) { + + // recurse on children statements + List<Integer> childVars = new ArrayList<Integer>(); + List<Exprent> currVars = new ArrayList<Exprent>(); + + for(Object obj: stat.getSequentialObjects()) { + if(obj instanceof Statement) { + Statement st = (Statement)obj; + childVars.addAll(initStatement(st)); + + if(st.type == DoStatement.TYPE_DO) { + DoStatement dost = (DoStatement)st; + if(dost.getLooptype() != DoStatement.LOOP_FOR && + dost.getLooptype() != DoStatement.LOOP_DO) { + currVars.add(dost.getConditionExprent()); + } + } else if(st.type == DoStatement.TYPE_CATCHALL) { + CatchAllStatement fin = (CatchAllStatement)st; + if(fin.isFinally() && fin.getMonitor() != null) { + currVars.add(fin.getMonitor()); + } + } + } else if(obj instanceof Exprent) { + currVars.add((Exprent)obj); + } + } + + // children statements + for(Integer index: childVars) { + Integer count = mapCount.get(index); + if(count == null) { + count = new Integer(0); + } + mapCount.put(index, new Integer(count.intValue()+1)); + } + + condlst = getAllVars(currVars); + } else { + condlst = getAllVars(stat.getExprents()); + } + + // this statement + for(VarExprent var: condlst) { + mapCount.put(new Integer(var.getIndex()), new Integer(2)); + } + + + HashSet<Integer> set = new HashSet<Integer>(mapCount.keySet()); + + // put all variables defined in this statement into the set + Iterator<Entry<Integer, Integer>> itMult = mapCount.entrySet().iterator(); + while(itMult.hasNext()) { + Entry<Integer, Integer> en = itMult.next(); + if(en.getValue().intValue()>1) { + mapVarDefStatements.put(en.getKey(), stat); + } + } + + mapStatementVars.put(stat.id, set); + + return set; + } + + private List<VarExprent> getAllVars(List<Exprent> lst) { + + List<VarExprent> res = new ArrayList<VarExprent>(); + List<Exprent> listTemp = new ArrayList<Exprent>(); + + for(Exprent expr: lst) { + listTemp.addAll(expr.getAllExprents(true)); + listTemp.add(expr); + } + + for(Exprent exprent: listTemp) { + if(exprent.type == Exprent.EXPRENT_VAR) { + res.add((VarExprent)exprent); + } + } + + return res; + } + + private boolean setDefinition(Exprent expr, Integer index) { + if(expr.type == Exprent.EXPRENT_ASSIGNMENT) { + Exprent left = ((AssignmentExprent)expr).getLeft(); + if(left.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)left; + if(var.getIndex() == index.intValue()) { + var.setDefinition(true); + return true; + } + } + } + return false; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java new file mode 100644 index 0000000..6af5720 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java @@ -0,0 +1,135 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.VarType; + + +public class VarProcessor { + + private HashMap<VarVersionPaar, String> mapVarNames = new HashMap<VarVersionPaar, String>(); + + private VarVersionsProcessor varvers; + + private HashMap<VarVersionPaar, String> thisvars = new HashMap<VarVersionPaar, String>(); + + private HashSet<VarVersionPaar> externvars = new HashSet<VarVersionPaar>(); + + public void setVarVersions(RootStatement root) { + + varvers = new VarVersionsProcessor(); + varvers.setVarVersions(root); + } + + public void setVarDefinitions(Statement root) { + mapVarNames = new HashMap<VarVersionPaar, String>(); + + VarDefinitionHelper defproc = new VarDefinitionHelper(root, + (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD), this); + defproc.setVarDefinitions(); + } + + public void setDebugVarNames(HashMap<Integer, String> mapDebugVarNames) { + + if(varvers == null) { + return; + } + + HashMap<Integer, Integer> mapOriginalVarIndices = varvers.getMapOriginalVarIndices(); + + List<VarVersionPaar> listVars = new ArrayList<VarVersionPaar>(mapVarNames.keySet()); + Collections.sort(listVars, new Comparator<VarVersionPaar>() { + public int compare(VarVersionPaar o1, VarVersionPaar o2) { + return o1.var>o2.var?1:(o1.var==o2.var?0:-1); + } + }); + + HashMap<String, Integer> mapNames = new HashMap<String, Integer>(); + + for(VarVersionPaar varpaar : listVars) { + String name = mapVarNames.get(varpaar); + + Integer orindex = mapOriginalVarIndices.get(varpaar.var); + if(orindex != null && mapDebugVarNames.containsKey(orindex)) { + name = mapDebugVarNames.get(orindex); + } + + Integer counter = mapNames.get(name); + mapNames.put(name, counter==null?counter = new Integer(0):++counter); + + if(counter > 0) { + name+=String.valueOf(counter); + } + + mapVarNames.put(varpaar, name); + } + + } + + public void refreshVarNames(VarNamesCollector vc) { + + HashMap<VarVersionPaar, String> tempVarNames = new HashMap<VarVersionPaar, String>(mapVarNames); + for(Entry<VarVersionPaar, String> ent: tempVarNames.entrySet()) { + mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue())); + } + } + + + public VarType getVarType(VarVersionPaar varpaar) { + return varvers==null?null:varvers.getVarType(varpaar); + } + + public void setVarType(VarVersionPaar varpaar, VarType type) { + varvers.setVarType(varpaar, type); + } + + public String getVarName(VarVersionPaar varpaar) { + return mapVarNames==null?null:mapVarNames.get(varpaar); + } + + public void setVarName(VarVersionPaar varpaar, String name) { + mapVarNames.put(varpaar, name); + } + + public int getVarFinal(VarVersionPaar varpaar) { + return varvers==null?VarTypeProcessor.VAR_FINAL:varvers.getVarFinal(varpaar); + } + + public void setVarFinal(VarVersionPaar varpaar, int finaltype) { + varvers.setVarFinal(varpaar, finaltype); + } + + public HashMap<VarVersionPaar, String> getThisvars() { + return thisvars; + } + + public HashSet<VarVersionPaar> getExternvars() { + return externvars; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java new file mode 100644 index 0000000..3e2e2b3 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java @@ -0,0 +1,273 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +public class VarTypeProcessor { + + public static final int VAR_NONFINAL = 1; + public static final int VAR_FINALEXPLICIT = 2; + public static final int VAR_FINAL = 3; + + private HashMap<VarVersionPaar, VarType> mapExprentMinTypes = new HashMap<VarVersionPaar, VarType>(); + + private HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = new HashMap<VarVersionPaar, VarType>(); + + private HashMap<VarVersionPaar, Integer> mapFinalVars = new HashMap<VarVersionPaar, Integer>(); + + private void setInitVars(RootStatement root) { + + StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + + // method descriptor + boolean thisvar = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0; + + MethodDescriptor md = (MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR); + + if(thisvar) { + VarType cltype = new VarType(CodeConstants.TYPE_OBJECT, 0, + ((StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS)).qualifiedName); + mapExprentMinTypes.put(new VarVersionPaar(0,1), cltype); + mapExprentMaxTypes.put(new VarVersionPaar(0,1), cltype); + } + + int varindex = 0; + for(int i=0;i<md.params.length;i++) { + mapExprentMinTypes.put(new VarVersionPaar(varindex+(thisvar?1:0), 1), md.params[i]); + mapExprentMaxTypes.put(new VarVersionPaar(varindex+(thisvar?1:0), 1), md.params[i]); + varindex+=md.params[i].stack_size; + } + + // catch variables + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(root); + + while(!stack.isEmpty()) { + Statement stat = stack.removeFirst(); + + List<VarExprent> lstVars = null; + if(stat.type == Statement.TYPE_CATCHALL) { + lstVars = ((CatchAllStatement)stat).getVars(); + } else if(stat.type == Statement.TYPE_TRYCATCH) { + lstVars = ((CatchStatement)stat).getVars(); + } + + if(lstVars != null) { + for(VarExprent var: lstVars) { + mapExprentMinTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); + mapExprentMaxTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype()); + } + } + + stack.addAll(stat.getStats()); + } + } + + public void calculateVarTypes(RootStatement root, DirectGraph dgraph) { + + setInitVars(root); + + resetExprentTypes(dgraph); + + while(!processVarTypes(dgraph)); + } + + private void resetExprentTypes(DirectGraph dgraph) { + + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for(Exprent expr: lst) { + if(expr.type == Exprent.EXPRENT_VAR) { + ((VarExprent)expr).setVartype(VarType.VARTYPE_UNKNOWN); + } else if(expr.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)expr; + if(cexpr.getConsttype().type_family == CodeConstants.TYPE_FAMILY_INTEGER) { + cexpr.setConsttype(new ConstExprent(cexpr.getIntValue(), cexpr.isBoolPermitted()).getConsttype()); + } + } + } + return 0; + } + }); + } + + private boolean processVarTypes(DirectGraph dgraph) { + + return dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + return checkTypeExprent(exprent)?0:1; + } + }); + } + + + private boolean checkTypeExprent(Exprent exprent) { + + for(Exprent expr: exprent.getAllExprents()) { + if(!checkTypeExprent(expr)) { + return false; + } + } + + if(exprent.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)exprent; + if(cexpr.getConsttype().type_family <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer + VarVersionPaar cpaar = new VarVersionPaar(cexpr.id, -1); + if(!mapExprentMinTypes.containsKey(cpaar)) { + mapExprentMinTypes.put(cpaar, cexpr.getConsttype()); + } + } + } + + CheckTypesResult result = exprent.checkExprTypeBounds(); + + for(CheckTypesResult.ExprentTypePair entry: result.getLstMaxTypeExprents()) { + if(entry.type.type_family != CodeConstants.TYPE_FAMILY_OBJECT) { + changeExprentType(entry.exprent, entry.type, 1); + } + } + + boolean res = true; + for(CheckTypesResult.ExprentTypePair entry: result.getLstMinTypeExprents()) { + res &= changeExprentType(entry.exprent, entry.type, 0); + } + + return res; + } + + + private boolean changeExprentType(Exprent exprent, VarType newtype, int minmax) { + + boolean res = true; + + switch(exprent.type) { + case Exprent.EXPRENT_CONST: + ConstExprent cexpr = (ConstExprent)exprent; + VarType consttype = cexpr.getConsttype(); + + if(newtype.type_family > CodeConstants.TYPE_FAMILY_INTEGER || consttype.type_family > CodeConstants.TYPE_FAMILY_INTEGER) { + return true; + } else if(newtype.type_family == CodeConstants.TYPE_FAMILY_INTEGER) { + VarType mininteger = new ConstExprent((Integer)((ConstExprent)exprent).getValue(), false).getConsttype(); + if(mininteger.isStrictSuperset(newtype)) { + newtype = mininteger; + } + } + case Exprent.EXPRENT_VAR: + VarVersionPaar varpaar = null; + if(exprent.type == Exprent.EXPRENT_CONST) { + varpaar = new VarVersionPaar(((ConstExprent)exprent).id, -1); + } else if(exprent.type == Exprent.EXPRENT_VAR) { + varpaar = new VarVersionPaar((VarExprent)exprent); + } + + if(minmax == 0) { // min + VarType currentMinType = mapExprentMinTypes.get(varpaar); + VarType newMinType; + if(currentMinType==null || newtype.type_family > currentMinType.type_family) { + newMinType = newtype; + } else if(newtype.type_family < currentMinType.type_family) { + return true; + } else { + newMinType = VarType.getCommonSupertype(currentMinType, newtype); + } + + mapExprentMinTypes.put(varpaar, newMinType); + if(exprent.type == Exprent.EXPRENT_CONST) { + ((ConstExprent)exprent).setConsttype(newMinType); + } + + if(currentMinType != null && (newMinType.type_family > currentMinType.type_family || + newMinType.isStrictSuperset(currentMinType))) { + return false; + } + } else { // max + VarType currentMaxType = mapExprentMaxTypes.get(varpaar); + VarType newMaxType; + if(currentMaxType==null || newtype.type_family < currentMaxType.type_family) { + newMaxType = newtype; + } else if(newtype.type_family > currentMaxType.type_family) { + return true; + } else { + newMaxType = VarType.getCommonMinType(currentMaxType, newtype); + } + + mapExprentMaxTypes.put(varpaar, newMaxType); + } + break; + case Exprent.EXPRENT_ASSIGNMENT: + return changeExprentType(((AssignmentExprent)exprent).getRight(), newtype, minmax); + case Exprent.EXPRENT_FUNCTION: + FunctionExprent func = (FunctionExprent)exprent; + switch(func.getFunctype()){ + case FunctionExprent.FUNCTION_IIF: // FIXME: + res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); + res &= changeExprentType(func.getLstOperands().get(2), newtype, minmax); + break; + case FunctionExprent.FUNCTION_AND: + case FunctionExprent.FUNCTION_OR: + case FunctionExprent.FUNCTION_XOR: + res &= changeExprentType(func.getLstOperands().get(0), newtype, minmax); + res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax); + } + } + + return res; + } + + public HashMap<VarVersionPaar, VarType> getMapExprentMaxTypes() { + return mapExprentMaxTypes; + } + + public HashMap<VarVersionPaar, VarType> getMapExprentMinTypes() { + return mapExprentMinTypes; + } + + public HashMap<VarVersionPaar, Integer> getMapFinalVars() { + return mapFinalVars; + } + + public void setVarType(VarVersionPaar varpaar, VarType type) { + mapExprentMinTypes.put(varpaar, type); + } + + public VarType getVarType(VarVersionPaar varpaar) { + return mapExprentMinTypes.get(varpaar); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java new file mode 100644 index 0000000..cac86ef --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java @@ -0,0 +1,56 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +public class VarVersionEdge { // FIXME: can be removed? + + public static final int EDGE_GENERAL = 0; + public static final int EDGE_PHANTOM = 1; + + public int type; + + public VarVersionNode source; + + public VarVersionNode dest; + + private int hashCode; + + public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) { + this.type = type; + this.source = source; + this.dest = dest; + this.hashCode = source.hashCode() ^ dest.hashCode() + type; + } + + @Override + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof VarVersionEdge)) return false; + + VarVersionEdge edge = (VarVersionEdge)o; + return type == edge.type && source == edge.source && dest == edge.dest; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + return source.toString() + " ->" + type + "-> " + dest.toString(); + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java new file mode 100644 index 0000000..7e2b69b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java @@ -0,0 +1,80 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode; +import org.jetbrains.java.decompiler.util.SFormsFastMapDirect; + +public class VarVersionNode implements IGraphNode { + + public static final int FLAG_PHANTOM_FINEXIT = 2; + + public int var; + + public int version; + + public Set<VarVersionEdge> succs = new HashSet<VarVersionEdge>(); + + public Set<VarVersionEdge> preds = new HashSet<VarVersionEdge>(); + + public int flags; + + public SFormsFastMapDirect live = new SFormsFastMapDirect(); + + + public VarVersionNode(int var, int version) { + this.var = var; + this.version = version; + } + + public VarVersionPaar getVarPaar() { + return new VarVersionPaar(var, version); + } + + public List<IGraphNode> getPredecessors() { + List<IGraphNode> lst = new ArrayList<IGraphNode>(preds.size()); + for(VarVersionEdge edge : preds) { + lst.add(edge.source); + } + return lst; + } + + public void removeSuccessor(VarVersionEdge edge) { + succs.remove(edge); + } + + public void removePredecessor(VarVersionEdge edge) { + preds.remove(edge); + } + + public void addSuccessor(VarVersionEdge edge) { + succs.add(edge); + } + + public void addPredecessor(VarVersionEdge edge) { + preds.add(edge); + } + + @Override + public String toString() { + return "("+var+"_"+version+")"; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java new file mode 100644 index 0000000..b9a3cec --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java @@ -0,0 +1,65 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; + +public class VarVersionPaar { + + public int var; + public int version; + + private int hashCode = -1; + + public VarVersionPaar(int var, int version) { + this.var = var; + this.version = version; + } + + public VarVersionPaar(Integer var, Integer version) { + this.var = var.intValue(); + this.version = version.intValue(); + } + + public VarVersionPaar(VarExprent var) { + this.var = var.getIndex(); + this.version = var.getVersion(); + } + + @Override + public boolean equals(Object o) { + if(o == this) return true; + if(o == null || !(o instanceof VarVersionPaar)) return false; + + VarVersionPaar paar = (VarVersionPaar)o; + return var == paar.var && version == paar.version; + } + + @Override + public int hashCode() { + if(hashCode == -1) { + hashCode = this.var * 3 + this.version; + } + return hashCode; + } + + @Override + public String toString() { + return "("+var+","+version+")"; + } + + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java new file mode 100644 index 0000000..2ad672b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java @@ -0,0 +1,172 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +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.util.VBStyleCollection; + + +public class VarVersionsGraph { + + public int counter = 0; + + public VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = new VBStyleCollection<VarVersionNode, VarVersionPaar>(); + + private GenericDominatorEngine engine; + + public VarVersionNode createNode(VarVersionPaar ver) { + VarVersionNode node; + nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver); + return node; + } + + public void addNodes(Collection<VarVersionNode> colnodes, Collection<VarVersionPaar> colpaars) { + nodes.addAllWithKey(colnodes, colpaars); + } + + public boolean isDominatorSet(VarVersionNode node, HashSet<VarVersionNode> domnodes) { + + if(domnodes.size() == 1) { + return engine.isDominator(node, domnodes.iterator().next()); + } else { + + HashSet<VarVersionNode> marked = new HashSet<VarVersionNode>(); + + if(domnodes.contains(node)) { + return true; + } + + LinkedList<VarVersionNode> lstNodes = new LinkedList<VarVersionNode>(); + lstNodes.add(node); + + while(!lstNodes.isEmpty()) { + + VarVersionNode nd = lstNodes.remove(0); + if(marked.contains(nd)) { + continue; + } else { + marked.add(nd); + } + + if(nd.preds.isEmpty()) { + return false; + } + + for(VarVersionEdge edge: nd.preds) { + VarVersionNode pred = edge.source; + if(!marked.contains(pred) && !domnodes.contains(pred)) { + lstNodes.add(pred); + } + } + } + } + + return true; + } + + public void initDominators() { + + final HashSet<VarVersionNode> roots = new HashSet<VarVersionNode>(); + + for(VarVersionNode node: nodes) { + if(node.preds.isEmpty()) { + roots.add(node); + } + } + + engine = new GenericDominatorEngine(new IGraph() { + public List<? extends IGraphNode> getReversePostOrderList() { + return getReversedPostOrder(roots); + } + + public Set<? extends IGraphNode> getRoots() { + return new HashSet<IGraphNode>(roots); + } + }); + + engine.initialize(); + } + + private LinkedList<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) { + + LinkedList<VarVersionNode> lst = new LinkedList<VarVersionNode>(); + HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>(); + + for(VarVersionNode root: roots) { + + LinkedList<VarVersionNode> lstTemp = new LinkedList<VarVersionNode>(); + addToReversePostOrderListIterative(root, lstTemp, setVisited); + + lst.addAll(lstTemp); + } + + return lst; + } + + private void addToReversePostOrderListIterative(VarVersionNode root, List<VarVersionNode> lst, HashSet<VarVersionNode> setVisited) { + + HashMap<VarVersionNode, List<VarVersionEdge>> mapNodeSuccs = new HashMap<VarVersionNode, List<VarVersionEdge>>(); + + LinkedList<VarVersionNode> stackNode = new LinkedList<VarVersionNode>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + stackNode.add(root); + stackIndex.add(0); + + while(!stackNode.isEmpty()) { + + VarVersionNode node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<VarVersionEdge> lstSuccs = mapNodeSuccs.get(node); + if(lstSuccs == null) { + mapNodeSuccs.put(node, lstSuccs = new ArrayList<VarVersionEdge>(node.succs)); + } + + for(;index<lstSuccs.size();index++) { + VarVersionNode succ = lstSuccs.get(index).dest; + + 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(); + } + } + + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java new file mode 100644 index 0000000..5e2adca --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java @@ -0,0 +1,340 @@ +/* + * 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 org.jetbrains.java.decompiler.modules.decompiler.vars; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; +import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet; + +public class VarVersionsProcessor { + + private HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>(); + + private VarTypeProcessor typeproc; + + public void setVarVersions(RootStatement root) { + + StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + + SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); + ssa.splitVariables(root, mt); + + FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); + DirectGraph dgraph = flatthelper.buildDirectGraph(root); + +// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + mergePhiVersions(ssa, dgraph); + +// System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava()); + + typeproc = new VarTypeProcessor(); + typeproc.calculateVarTypes(root, dgraph); + + simpleMerge(typeproc, dgraph, mt); + + // FIXME: advanced merging + + eliminateNonJavaTypes(typeproc); + + setNewVarIndices(typeproc, dgraph); + } + + private void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph dgraph) { + + // collect phi versions + List<HashSet<VarVersionPaar>> lst = new ArrayList<HashSet<VarVersionPaar>>(); + for(Entry<VarVersionPaar, FastSparseSet<Integer>> ent: ssa.getPhi().entrySet()) { + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(); + set.add(ent.getKey()); + for(Integer vers: ent.getValue()) { + set.add(new VarVersionPaar(ent.getKey().var, vers.intValue())); + } + + for(int i=lst.size()-1;i>=0;i--) { + HashSet<VarVersionPaar> tset = lst.get(i); + HashSet<VarVersionPaar> intersection = new HashSet<VarVersionPaar>(set); + intersection.retainAll(tset); + + if(!intersection.isEmpty()) { + set.addAll(tset); + lst.remove(i); + } + } + + lst.add(set); + } + + final HashMap<VarVersionPaar, Integer> phivers = new HashMap<VarVersionPaar, Integer>(); + for(HashSet<VarVersionPaar> set: lst) { + int min = Integer.MAX_VALUE; + for(VarVersionPaar paar: set) { + if(paar.version<min) { + min = paar.version; + } + } + + for(VarVersionPaar paar: set) { + phivers.put(new VarVersionPaar(paar.var, paar.version), min); + } + } + + + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for(Exprent expr: lst) { + if(expr.type == Exprent.EXPRENT_VAR) { + VarExprent var = (VarExprent)expr; + Integer vers = phivers.get(new VarVersionPaar(var)); + if(vers != null) { + var.setVersion(vers); + } + } + } + return 0; + } + }); + + } + + private void eliminateNonJavaTypes(VarTypeProcessor typeproc) { + + HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); + HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet()); + for(VarVersionPaar paar: set) { + VarType type = mapExprentMinTypes.get(paar); + VarType maxtype = mapExprentMaxTypes.get(paar); + + if(type.type == CodeConstants.TYPE_BYTECHAR || type.type == CodeConstants.TYPE_SHORTCHAR) { + if(maxtype != null && maxtype.type == CodeConstants.TYPE_CHAR) { + type = VarType.VARTYPE_CHAR; + } else { + type = type.type == CodeConstants.TYPE_BYTECHAR?VarType.VARTYPE_BYTE:VarType.VARTYPE_SHORT; + } + mapExprentMinTypes.put(paar, type); + //} else if(type.type == CodeConstants.TYPE_CHAR && (maxtype == null || maxtype.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int + // mapExprentMinTypes.put(paar, VarType.VARTYPE_INT); + } else if(type.type == CodeConstants.TYPE_NULL) { + mapExprentMinTypes.put(paar, VarType.VARTYPE_OBJECT); + } + } + + } + + private void simpleMerge(VarTypeProcessor typeproc, DirectGraph dgraph, StructMethod mt) { + + HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); + HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + + HashMap<Integer, HashSet<Integer>> mapVarVersions = new HashMap<Integer, HashSet<Integer>>(); + + for(VarVersionPaar varpaar: mapExprentMinTypes.keySet()) { + if(varpaar.version >= 0) { // don't merge constants + HashSet<Integer> set = mapVarVersions.get(varpaar.var); + if(set == null) { + set = new HashSet<Integer>(); + mapVarVersions.put(varpaar.var, set); + } + set.add(varpaar.version); + } + } + + boolean is_method_static = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) != 0; + + final HashMap<VarVersionPaar, Integer> mapMergedVersions = new HashMap<VarVersionPaar, Integer>(); + + for(Entry<Integer, HashSet<Integer>> ent: mapVarVersions.entrySet()) { + + if(ent.getValue().size() > 1) { + List<Integer> lstVersions = new ArrayList<Integer>(ent.getValue()); + Collections.sort(lstVersions); + + for(int i=0;i<lstVersions.size();i++) { + VarVersionPaar firstpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(i)); + VarType firsttype = mapExprentMinTypes.get(firstpaar); + + if(firstpaar.var == 0 && firstpaar.version == 1 && !is_method_static) { + continue; // don't merge 'this' variable + } + + for(int j=i+1;j<lstVersions.size();j++) { + VarVersionPaar secpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(j)); + VarType sectype = mapExprentMinTypes.get(secpaar); + + if(firsttype.equals(sectype) || (firsttype.equals(VarType.VARTYPE_NULL) && sectype.type == CodeConstants.TYPE_OBJECT) + || (sectype.equals(VarType.VARTYPE_NULL) && firsttype.type == CodeConstants.TYPE_OBJECT)) { + + VarType firstMaxType = mapExprentMaxTypes.get(firstpaar); + VarType secMaxType = mapExprentMaxTypes.get(secpaar); + mapExprentMaxTypes.put(firstpaar, firstMaxType==null?secMaxType: + (secMaxType==null?firstMaxType:VarType.getCommonMinType(firstMaxType, secMaxType))); + + + mapMergedVersions.put(secpaar, firstpaar.version); + mapExprentMaxTypes.remove(secpaar); + mapExprentMinTypes.remove(secpaar); + + if(firsttype.equals(VarType.VARTYPE_NULL)) { + mapExprentMinTypes.put(firstpaar, sectype); + firsttype = sectype; + } + + typeproc.getMapFinalVars().put(firstpaar, VarTypeProcessor.VAR_NONFINAL); + + lstVersions.remove(j); + j--; + } + + } + } + } + } + + if(!mapMergedVersions.isEmpty()) { + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for(Exprent expr: lst) { + if(expr.type == Exprent.EXPRENT_VAR) { + VarExprent varex = (VarExprent)expr; + Integer newversion = mapMergedVersions.get(new VarVersionPaar(varex)); + if(newversion != null) { + varex.setVersion(newversion); + } + } + } + + return 0; + } + }); + } + + } + + private void setNewVarIndices(VarTypeProcessor typeproc, DirectGraph dgraph) { + + final HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes(); + HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes(); + HashMap<VarVersionPaar, Integer> mapFinalVars = typeproc.getMapFinalVars(); + + CounterContainer ccon = DecompilerContext.getCountercontainer(); + + final HashMap<VarVersionPaar, Integer> mapVarPaar = new HashMap<VarVersionPaar, Integer>(); + HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>(); + + // map var-version paars on new var indexes + HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet()); + for(VarVersionPaar vpaar: set) { + + if(vpaar.version >= 0) { + int newindex = vpaar.version == 1?vpaar.var: + ccon.getCounterAndIncrement(CounterContainer.VAR_COUNTER); + + VarVersionPaar newvar = new VarVersionPaar(newindex, 0); + + mapExprentMinTypes.put(newvar, mapExprentMinTypes.get(vpaar)); + mapExprentMaxTypes.put(newvar, mapExprentMaxTypes.get(vpaar)); + + if(mapFinalVars.containsKey(vpaar)) { + mapFinalVars.put(newvar, mapFinalVars.remove(vpaar)); + } + + mapVarPaar.put(vpaar, newindex); + mapOriginalVarIndices.put(newindex, vpaar.var); + } + } + + // set new vars + dgraph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for(Exprent expr: lst) { + if(expr.type == Exprent.EXPRENT_VAR) { + VarExprent varex = (VarExprent)expr; + Integer newvarindex = mapVarPaar.get(new VarVersionPaar(varex)); + if(newvarindex != null) { + varex.setIndex(newvarindex); + varex.setVersion(0); + } + } else if(expr.type == Exprent.EXPRENT_CONST) { + VarType maxType = mapExprentMaxTypes.get(new VarVersionPaar(expr.id, -1)); + if(maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) { + ((ConstExprent)expr).setConsttype(maxType); + } + } + } + + return 0; + } + }); + + this.mapOriginalVarIndices = mapOriginalVarIndices; + } + + public VarType getVarType(VarVersionPaar varpaar) { + return typeproc==null?null:typeproc.getVarType(varpaar); + } + + public void setVarType(VarVersionPaar varpaar, VarType type) { + typeproc.setVarType(varpaar, type); + } + + public int getVarFinal(VarVersionPaar varpaar) { + + int ret = VarTypeProcessor.VAR_FINAL; + if(typeproc!=null) { + Integer fin = typeproc.getMapFinalVars().get(varpaar); + ret = fin==null?VarTypeProcessor.VAR_FINAL:fin.intValue(); + } + + return ret; + } + + public void setVarFinal(VarVersionPaar varpaar, int finaltype) { + typeproc.getMapFinalVars().put(varpaar, finaltype); + } + + public HashMap<Integer, Integer> getMapOriginalVarIndices() { + return mapOriginalVarIndices; + } + + +} diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java b/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java new file mode 100644 index 0000000..a3015a8 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java @@ -0,0 +1,41 @@ +package org.jetbrains.java.decompiler.modules.renamer; + +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.java.decompiler.struct.StructClass; + +public class ClassWrapperNode { + + private StructClass classStruct; + + private ClassWrapperNode superclass; + + private List<ClassWrapperNode> subclasses = new ArrayList<ClassWrapperNode>(); + + public ClassWrapperNode(StructClass cl) { + this.classStruct = cl; + } + + public void addSubclass(ClassWrapperNode node) { + node.setSuperclass(this); + subclasses.add(node); + } + + public StructClass getClassStruct() { + return classStruct; + } + + public List<ClassWrapperNode> getSubclasses() { + return subclasses; + } + + public ClassWrapperNode getSuperclass() { + return superclass; + } + + public void setSuperclass(ClassWrapperNode superclass) { + this.superclass = superclass; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java b/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java new file mode 100644 index 0000000..d85d51f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java @@ -0,0 +1,127 @@ +package org.jetbrains.java.decompiler.modules.renamer; + +import java.util.HashSet; + +import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; + +public class ConverterHelper implements IIdentifierRenamer { + + private static HashSet<String> setReserved = new HashSet<String>(); + + static { + setReserved.add("abstract"); + setReserved.add("do"); + setReserved.add("if"); + setReserved.add("package"); + setReserved.add("synchronized"); + setReserved.add("boolean"); + setReserved.add("double"); + setReserved.add("implements"); + setReserved.add("private"); + setReserved.add("this"); + setReserved.add("break"); + setReserved.add("else"); + setReserved.add("import"); + setReserved.add("protected"); + setReserved.add("throw"); + setReserved.add("byte"); + setReserved.add("extends"); + setReserved.add("instanceof"); + setReserved.add("public"); + setReserved.add("throws"); + setReserved.add("case"); + setReserved.add("false"); + setReserved.add("int"); + setReserved.add("return"); + setReserved.add("transient"); + setReserved.add("catch"); + setReserved.add("final"); + setReserved.add("interface"); + setReserved.add("short"); + setReserved.add("true"); + setReserved.add("char"); + setReserved.add("finally"); + setReserved.add("long"); + setReserved.add("static"); + setReserved.add("try"); + setReserved.add("class"); + setReserved.add("float"); + setReserved.add("native"); + setReserved.add("strictfp"); + setReserved.add("void"); + setReserved.add("const"); + setReserved.add("for"); + setReserved.add("new"); + setReserved.add("super"); + setReserved.add("volatile"); + setReserved.add("continue"); + setReserved.add("goto"); + setReserved.add("null"); + setReserved.add("switch"); + setReserved.add("while"); + setReserved.add("default"); + setReserved.add("assert"); + setReserved.add("enum"); + } + + private int class_counter = 0; + + private int field_counter = 0; + + private int method_counter = 0; + + private HashSet<String> setNonStandardClassNames = new HashSet<String>(); + + public boolean toBeRenamed(int element_type, String classname, String element, String descriptor) { + String value = (element_type == IIdentifierRenamer.ELEMENT_CLASS)?classname:element; + return value == null || value.length() == 0 || value.length()<=2 || setReserved.contains(value) || Character.isDigit(value.charAt(0)); + } + + // TODO: consider possible conflicts with not renamed classes, fields and methods! + // We should get all relevant information here. + public String getNextClassname(String fullname, String shortname) { + + if(shortname == null) { + return "class_"+(class_counter++); + } + + int index = 0; + while(Character.isDigit(shortname.charAt(index))) { + index++; + } + + if(index == 0 || index == shortname.length()) { + return "class_"+(class_counter++); + } else { + String name = shortname.substring(index); + + if(setNonStandardClassNames.contains(name)) { + return "Inner"+name+"_"+(class_counter++); + } else { + setNonStandardClassNames.add(name); + return "Inner"+name; + } + } + } + + public String getNextFieldname(String classname, String field, String descriptor) { + return "field_"+(field_counter++); + } + + public String getNextMethodname(String classname, String method, String descriptor) { + return "method_"+(method_counter++); + } + + // ***************************************************************************** + // static methods + // ***************************************************************************** + + public static String getSimpleClassName(String fullname) { + return fullname.substring(fullname.lastIndexOf('/')+1); + } + + public static String replaceSimpleClassName(String fullname, String newname) { + return fullname.substring(0, fullname.lastIndexOf('/')+1)+newname; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java new file mode 100644 index 0000000..634dc59 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java @@ -0,0 +1,447 @@ +package org.jetbrains.java.decompiler.modules.renamer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructContext; +import org.jetbrains.java.decompiler.struct.StructField; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.VBStyleCollection; + +public class IdentifierConverter { + + private StructContext context; + + private IIdentifierRenamer helper; + + private PoolInterceptor interceptor; + + private List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); + + private List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); + + private HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>(); + + public void rename(StructContext context) { + + try { + this.context = context; + + String user_class = (String)DecompilerContext.getProperty(IFernflowerPreferences.USER_RENAMER_CLASS); + if(user_class != null) { + try { + helper = (IIdentifierRenamer)IdentifierConverter.class.getClassLoader().loadClass(user_class).newInstance(); + } catch(Exception ex) { + ; // ignore errors + } + } + + if(helper == null) { + helper = new ConverterHelper(); + } + + interceptor = new PoolInterceptor(helper); + + buildInheritanceTree(); + + renameAllClasses(); + + renameInterfaces(); + + renameClasses(); + + DecompilerContext.setPoolInterceptor(interceptor); + context.reloadContext(); + + } catch(IOException ex){ + throw new RuntimeException("Renaming failed!"); + } + + } + + private void renameClasses() { + + List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses); + + HashMap<String, HashMap<String, String>> classNameMaps = new HashMap<String, HashMap<String, String>>(); + + for(ClassWrapperNode node : lstClasses) { + + StructClass cl = node.getClassStruct(); + HashMap<String, String> names = new HashMap<String, String>(); + + // merge informations on super class + if(cl.superClass != null) { + HashMap<String, String> mapClass = classNameMaps.get(cl.superClass.getString()); + if(mapClass != null) { + names.putAll(mapClass); + } + } + + // merge informations on interfaces + for(String intrName : cl.getInterfaceNames()) { + HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); + if(mapInt != null) { + names.putAll(mapInt); + } else { + StructClass clintr = context.getClass(intrName); + if(clintr!=null) { + names.putAll(processExternalInterface(clintr)); + } + } + } + + renameClassIdentifiers(cl, names); + + if(!node.getSubclasses().isEmpty()) { + classNameMaps.put(cl.qualifiedName, names); + } + } + + } + + private HashMap<String, String> processExternalInterface(StructClass cl) { + + HashMap<String, String> names = new HashMap<String, String>(); + + for(String intrName : cl.getInterfaceNames()) { + + HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); + if(mapInt != null) { + names.putAll(mapInt); + } else { + StructClass clintr = context.getClass(intrName); + if(clintr!=null) { + names.putAll(processExternalInterface(clintr)); + } + } + } + + renameClassIdentifiers(cl, names); + + return names; + } + + private void renameInterfaces() { + + List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces); + + HashMap<String, HashMap<String, String>> interfaceNameMaps = new HashMap<String, HashMap<String, String>>(); + + // rename methods and fields + for(ClassWrapperNode node : lstInterfaces) { + + StructClass cl = node.getClassStruct(); + HashMap<String, String> names = new HashMap<String, String>(); + + // merge informations on super interfaces + for(String intrName : cl.getInterfaceNames()) { + HashMap<String, String> mapInt = interfaceNameMaps.get(intrName); + if(mapInt != null) { + names.putAll(mapInt); + } + } + + renameClassIdentifiers(cl, names); + + interfaceNameMaps.put(cl.qualifiedName, names); + } + + this.interfaceNameMaps = interfaceNameMaps; + } + + private void renameAllClasses() { + + // order not important + List<ClassWrapperNode> lstAllClasses = new ArrayList<ClassWrapperNode>(getReversePostOrderListIterative(rootInterfaces)); + lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses)); + + // rename all interfaces and classes + for(ClassWrapperNode node : lstAllClasses) { + renameClass(node.getClassStruct()); + } + } + + private void renameClass(StructClass cl) { + + if(!cl.isOwn()) { + return; + } + + String classOldFullName = cl.qualifiedName; + String classNewFullName = classOldFullName; + + // TODO: rename packages + String clsimplename = ConverterHelper.getSimpleClassName(classOldFullName); + if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_CLASS, clsimplename, null, null)) { + do { + classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, + helper.getNextClassname(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName))); + } while(context.getClasses().containsKey(classNewFullName)); + + interceptor.addName(classOldFullName, classNewFullName); + } + + } + + private void renameClassIdentifiers(StructClass cl, HashMap<String, String> names) { + + // all classes are already renamed + String classOldFullName = cl.qualifiedName; + String classNewFullName = interceptor.getName(classOldFullName); + + if(classNewFullName == null) { + classNewFullName = classOldFullName; + } + + // methods + HashSet<String> setMethodNames = new HashSet<String>(); + for(StructMethod md : cl.getMethods()) { + setMethodNames.add(md.getName()); + } + + VBStyleCollection<StructMethod, String> methods = cl.getMethods(); + for(int i=0;i<methods.size();i++) { + + StructMethod mt = methods.get(i); + String key = methods.getKey(i); + + int access_flags = mt.getAccessFlags(); + boolean isPrivate = ((access_flags & CodeConstants.ACC_PRIVATE) != 0); + + String name = mt.getName(); + if(!cl.isOwn() || (access_flags & CodeConstants.ACC_NATIVE) != 0) { + // external and native methods must not be renamed + if(!isPrivate) { + names.put(key, name); + } + } else if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) { + if(isPrivate || !names.containsKey(key)) { + do { + name = helper.getNextMethodname(classOldFullName, name, mt.getDescriptor()); + } while(setMethodNames.contains(name)); + + if(!isPrivate) { + names.put(key, name); + } + } else { + name = names.get(key); + } + + interceptor.addName(classOldFullName+" "+mt.getName()+" "+mt.getDescriptor(), + classNewFullName+" "+name+" "+buildNewDescriptor(false, mt.getDescriptor())); + } + } + + // external fields are not being renamed + if(!cl.isOwn()) { + return; + } + + // fields + // FIXME: should overloaded fields become the same name? + HashSet<String> setFieldNames = new HashSet<String>(); + for(StructField fd : cl.getFields()) { + setFieldNames.add(fd.getName()); + } + + for(StructField fd : cl.getFields()) { + if(helper.toBeRenamed(IIdentifierRenamer.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) { + String newname; + + do { + newname = helper.getNextFieldname(classOldFullName, fd.getName(), fd.getDescriptor()); + } while(setFieldNames.contains(newname)); + + interceptor.addName(classOldFullName+" "+fd.getName()+" "+fd.getDescriptor(), + classNewFullName+" "+newname+" "+buildNewDescriptor(true, fd.getDescriptor())); + } + } + + } + + private String buildNewDescriptor(boolean isField, String descriptor) { + + boolean updated = false; + + if(isField) { + FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor); + + VarType ftype = fd.type; + if(ftype.type == CodeConstants.TYPE_OBJECT) { + String newclname = interceptor.getName(ftype.value); + if(newclname != null) { + ftype.value = newclname; + updated = true; + } + } + + if(updated) { + return fd.getDescriptor(); + } + + } else { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor); + // params + for(VarType partype : md.params) { + if(partype.type == CodeConstants.TYPE_OBJECT) { + String newclname = interceptor.getName(partype.value); + if(newclname != null) { + partype.value = newclname; + updated = true; + } + } + } + + // return value + if(md.ret.type == CodeConstants.TYPE_OBJECT) { + String newclname = interceptor.getName(md.ret.value); + if(newclname!=null) { + md.ret.value = newclname; + updated = true; + } + } + + if(updated) { + return md.getDescriptor(); + } + } + + return descriptor; + } + + private List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) { + + List<ClassWrapperNode> res = new ArrayList<ClassWrapperNode>(); + + LinkedList<ClassWrapperNode> stackNode = new LinkedList<ClassWrapperNode>(); + LinkedList<Integer> stackIndex = new LinkedList<Integer>(); + + HashSet<ClassWrapperNode> setVisited = new HashSet<ClassWrapperNode>(); + + for(ClassWrapperNode root : roots) { + stackNode.add(root); + stackIndex.add(0); + } + + while(!stackNode.isEmpty()) { + + ClassWrapperNode node = stackNode.getLast(); + int index = stackIndex.removeLast(); + + setVisited.add(node); + + List<ClassWrapperNode> lstSubs = node.getSubclasses(); + + for(; index < lstSubs.size(); index++) { + ClassWrapperNode sub = lstSubs.get(index); + if(!setVisited.contains(sub)) { + stackIndex.add(index+1); + + stackNode.add(sub); + stackIndex.add(0); + + break; + } + } + + if(index == lstSubs.size()) { + res.add(0, node); + + stackNode.removeLast(); + } + } + + return res; + } + + + private void buildInheritanceTree() { + + HashMap<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>(); + HashMap<String, StructClass> classes = context.getClasses(); + + List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); + List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); + + for(StructClass cl : classes.values()) { + + if(!cl.isOwn()) { + continue; + } + + LinkedList<StructClass> stack = new LinkedList<StructClass>(); + LinkedList<ClassWrapperNode> stackSubnodes = new LinkedList<ClassWrapperNode>(); + + stack.add(cl); + stackSubnodes.add(null); + + while(!stack.isEmpty()) { + + StructClass clstr = stack.removeFirst(); + ClassWrapperNode child = stackSubnodes.removeFirst(); + + ClassWrapperNode node = nodes.get(clstr.qualifiedName); + boolean isNewNode = (node == null); + + if(isNewNode) { + nodes.put(clstr.qualifiedName, node = new ClassWrapperNode(clstr)); + } + + if(child!=null) { + node.addSubclass(child); + } + + if(!isNewNode) { + break; + } else { + + boolean isInterface = ((clstr.access_flags & CodeConstants.ACC_INTERFACE) != 0); + boolean found_parent = false; + + if(isInterface) { + for(String intrName : clstr.getInterfaceNames()) { + StructClass clparent = classes.get(intrName); + if(clparent != null) { + stack.add(clparent); + stackSubnodes.add(node); + found_parent = true; + } + } + } else { + if(clstr.superClass != null) { // null iff java/lang/Object + StructClass clparent = classes.get(clstr.superClass.getString()); + + if(clparent != null) { + stack.add(clparent); + stackSubnodes.add(node); + found_parent = true; + } + } + } + + if(!found_parent) { // no super class or interface + (isInterface?rootInterfaces:rootClasses).add(node); + } + } + } + } + + this.rootClasses = rootClasses; + this.rootInterfaces = rootInterfaces; + } + +} diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java b/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java new file mode 100644 index 0000000..73260f4 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java @@ -0,0 +1,36 @@ +package org.jetbrains.java.decompiler.modules.renamer; + +import java.util.HashMap; + +import org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer; + +public class PoolInterceptor { + + private IIdentifierRenamer helper; + + private HashMap<String, String> mapOldToNewNames = new HashMap<String, String>(); + + private HashMap<String, String> mapNewToOldNames = new HashMap<String, String>(); + + public PoolInterceptor(IIdentifierRenamer helper) { + this.helper = helper; + } + + public void addName(String oldName, String newName) { + mapOldToNewNames.put(oldName, newName); + mapNewToOldNames.put(newName, oldName); + } + + public String getName(String oldName) { + return mapOldToNewNames.get(oldName); + } + + public String getOldName(String newName) { + return mapNewToOldNames.get(newName); + } + + public IIdentifierRenamer getHelper() { + return helper; + } + +} |