diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java new file mode 100644 index 000000000000..1f4b5eea0e22 --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java @@ -0,0 +1,506 @@ +/* + * 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.stats.*; + +import java.util.*; +import java.util.Map.Entry; + + +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(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 { + 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 void processStatementLabel(Statement stat) { + processStatementLabel(stat, new HashSet<Statement>(), new HashSet<Statement>()); + } + + private static void processStatementLabel(Statement stat, Set<Statement> setBreak, Set<Statement> setContinue) { + if (stat.getExprents() == null) { + for (Statement st : stat.getStats()) { + processStatementLabel(st, setBreak, setContinue); + } + + 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); + } + + 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; + while (true) { + + 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; + } +} |