blob: b1a955cfd528e677e73aa7d6d367dd10dc07d6eb [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler;
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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
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) {
while (true) {
boolean changed = false;
for (Statement st : stat.getStats()) {
res |= mergeAllIfsRec(st, setReorderedIfs);
// collapse composed if's
if (changed = 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;
while (true) {
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 ? 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(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));
}
}
}