| /* |
| * 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; |
| } |
| } |