2 * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
4 package org.jetbrains.java.decompiler.modules.decompiler.stats;
6 import org.jetbrains.java.decompiler.code.CodeConstants;
7 import org.jetbrains.java.decompiler.code.InstructionSequence;
8 import org.jetbrains.java.decompiler.main.DecompilerContext;
9 import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
10 import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
11 import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
12 import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper;
13 import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
14 import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;
15 import org.jetbrains.java.decompiler.struct.match.IMatchable;
16 import org.jetbrains.java.decompiler.struct.match.MatchEngine;
17 import org.jetbrains.java.decompiler.struct.match.MatchNode;
18 import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
19 import org.jetbrains.java.decompiler.util.TextBuffer;
20 import org.jetbrains.java.decompiler.util.VBStyleCollection;
23 import java.util.Map.Entry;
25 public class Statement implements IMatchable {
26 public static final int STATEDGE_ALL = 0x80000000;
27 public static final int STATEDGE_DIRECT_ALL = 0x40000000;
29 public static final int DIRECTION_BACKWARD = 0;
30 public static final int DIRECTION_FORWARD = 1;
32 public static final int TYPE_GENERAL = 0;
33 public static final int TYPE_IF = 2;
34 public static final int TYPE_DO = 5;
35 public static final int TYPE_SWITCH = 6;
36 public static final int TYPE_TRY_CATCH = 7;
37 public static final int TYPE_BASIC_BLOCK = 8;
38 //public static final int TYPE_FINALLY = 9;
39 public static final int TYPE_SYNCHRONIZED = 10;
40 public static final int TYPE_PLACEHOLDER = 11;
41 public static final int TYPE_CATCH_ALL = 12;
42 public static final int TYPE_ROOT = 13;
43 public static final int TYPE_DUMMY_EXIT = 14;
44 public static final int TYPE_SEQUENCE = 15;
46 public static final int LASTBASICTYPE_IF = 0;
47 public static final int LASTBASICTYPE_SWITCH = 1;
48 public static final int LASTBASICTYPE_GENERAL = 2;
51 // *****************************************************************************
53 // *****************************************************************************
59 // *****************************************************************************
61 // *****************************************************************************
63 private final Map<Integer, List<StatEdge>> mapSuccEdges = new HashMap<>();
64 private final Map<Integer, List<StatEdge>> mapPredEdges = new HashMap<>();
66 private final Map<Integer, List<Statement>> mapSuccStates = new HashMap<>();
67 private final Map<Integer, List<Statement>> mapPredStates = new HashMap<>();
70 protected final VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<>();
72 protected Statement parent;
74 protected Statement first;
76 protected List<Exprent> exprents;
78 protected final HashSet<StatEdge> labelEdges = new HashSet<>();
80 protected final List<Exprent> varDefinitions = new ArrayList<>();
82 // copied statement, s. deobfuscating of irreducible CFGs
83 private boolean copied = false;
85 // relevant for the first stage of processing only
86 // set to null after initializing of the statement structure
88 protected Statement post;
90 protected int lastBasicType = LASTBASICTYPE_GENERAL;
92 protected boolean isMonitorEnter;
94 protected boolean containsMonitorExit;
96 protected HashSet<Statement> continueSet = new HashSet<>();
98 // *****************************************************************************
100 // *****************************************************************************
104 id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER);
107 Statement(int type) {
111 // *****************************************************************************
113 // *****************************************************************************
115 public void clearTempInformation() {
121 // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove
122 //lastBasicType = LASTBASICTYPE_GENERAL;
123 isMonitorEnter = false;
124 containsMonitorExit = false;
126 processMap(mapSuccEdges);
127 processMap(mapPredEdges);
128 processMap(mapSuccStates);
129 processMap(mapPredStates);
132 private static <T> void processMap(Map<Integer, List<T>> map) {
133 map.remove(StatEdge.TYPE_EXCEPTION);
135 List<T> lst = map.get(STATEDGE_DIRECT_ALL);
137 map.put(STATEDGE_ALL, new ArrayList<>(lst));
140 map.remove(STATEDGE_ALL);
144 public void collapseNodesToStatement(Statement stat) {
146 Statement head = stat.getFirst();
147 Statement post = stat.getPost();
149 VBStyleCollection<Statement, Integer> setNodes = stat.getStats();
153 for (StatEdge edge : post.getEdges(STATEDGE_DIRECT_ALL, DIRECTION_BACKWARD)) {
154 if (stat.containsStatementStrict(edge.getSource())) {
155 edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK);
156 stat.addLabeledEdge(edge);
161 // regular head edges
162 for (StatEdge prededge : head.getAllPredecessorEdges()) {
164 if (prededge.getType() != StatEdge.TYPE_EXCEPTION &&
165 stat.containsStatementStrict(prededge.getSource())) {
166 prededge.getSource().changeEdgeType(DIRECTION_FORWARD, prededge, StatEdge.TYPE_CONTINUE);
167 stat.addLabeledEdge(prededge);
170 head.removePredecessor(prededge);
171 prededge.getSource().changeEdgeNode(DIRECTION_FORWARD, prededge, stat);
172 stat.addPredecessor(prededge);
175 if (setNodes.containsKey(first.id)) {
180 Set<Statement> setHandlers = new HashSet<>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD));
181 for (Statement node : setNodes) {
182 setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD));
185 if (!setHandlers.isEmpty()) {
187 for (StatEdge edge : head.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) {
188 Statement handler = edge.getDestination();
190 if (setHandlers.contains(handler)) {
191 if (!setNodes.containsKey(handler.id)) {
192 stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions()));
197 for (Statement node : setNodes) {
198 for (StatEdge edge : node.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) {
199 if (setHandlers.contains(edge.getDestination())) {
200 node.removeSuccessor(edge);
207 !stat.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD).contains(post)) { // TODO: second condition redundant?
208 stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, post));
212 // adjust statement collection
213 for (Statement st : setNodes) {
214 stats.removeWithKey(st.id);
217 stats.addWithKey(stat, stat.id);
220 stat.setParent(this);
222 stat.buildContinueSet();
223 // monitorenter and monitorexit
224 stat.buildMonitorFlags();
226 if (stat.type == TYPE_SWITCH) {
227 // special case switch, sorting leaf nodes
228 ((SwitchStatement)stat).sortEdgesAndNodes();
232 public void setAllParent() {
233 for (Statement st : stats) {
238 public void addLabeledEdge(StatEdge edge) {
240 if (edge.closure != null) {
241 edge.closure.getLabelEdges().remove(edge);
244 this.getLabelEdges().add(edge);
247 private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) {
248 Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
249 Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
251 mapEdges.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(edge);
253 mapStates.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(direction == DIRECTION_BACKWARD ? edge.getSource() : edge.getDestination());
256 private void addEdgeInternal(int direction, StatEdge edge) {
257 int type = edge.getType();
260 if (type == StatEdge.TYPE_EXCEPTION) {
261 arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
264 arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
267 for (int edgetype : arrtypes) {
268 addEdgeDirectInternal(direction, edge, edgetype);
272 private void removeEdgeDirectInternal(int direction, StatEdge edge, int edgetype) {
274 Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
275 Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
277 List<StatEdge> lst = mapEdges.get(edgetype);
279 int index = lst.indexOf(edge);
282 mapStates.get(edgetype).remove(index);
287 private void removeEdgeInternal(int direction, StatEdge edge) {
289 int type = edge.getType();
292 if (type == StatEdge.TYPE_EXCEPTION) {
293 arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
296 arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
299 for (int edgetype : arrtypes) {
300 removeEdgeDirectInternal(direction, edge, edgetype);
304 public void addPredecessor(StatEdge edge) {
305 addEdgeInternal(DIRECTION_BACKWARD, edge);
308 public void removePredecessor(StatEdge edge) {
310 if (edge == null) { // FIXME: redundant?
314 removeEdgeInternal(DIRECTION_BACKWARD, edge);
317 public void addSuccessor(StatEdge edge) {
318 addEdgeInternal(DIRECTION_FORWARD, edge);
320 if (edge.closure != null) {
321 edge.closure.getLabelEdges().add(edge);
324 edge.getDestination().addPredecessor(edge);
327 public void removeSuccessor(StatEdge edge) {
333 removeEdgeInternal(DIRECTION_FORWARD, edge);
335 if (edge.closure != null) {
336 edge.closure.getLabelEdges().remove(edge);
339 if (edge.getDestination() != null) { // TODO: redundant?
340 edge.getDestination().removePredecessor(edge);
344 // TODO: make obsolete and remove
345 public void removeAllSuccessors(Statement stat) {
351 for (StatEdge edge : getAllSuccessorEdges()) {
352 if (edge.getDestination() == stat) {
353 removeSuccessor(edge);
358 public HashSet<Statement> buildContinueSet() {
361 for (Statement st : stats) {
362 continueSet.addAll(st.buildContinueSet());
364 continueSet.remove(st.getBasichead());
368 for (StatEdge edge : getEdges(StatEdge.TYPE_CONTINUE, DIRECTION_FORWARD)) {
369 continueSet.add(edge.getDestination().getBasichead());
372 if (type == TYPE_DO) {
373 continueSet.remove(first.getBasichead());
379 public void buildMonitorFlags() {
381 for (Statement st : stats) {
382 st.buildMonitorFlags();
386 case TYPE_BASIC_BLOCK:
387 BasicBlockStatement bblock = (BasicBlockStatement)this;
388 InstructionSequence seq = bblock.getBlock().getSeq();
390 if (seq != null && seq.length() > 0) {
391 for (int i = 0; i < seq.length(); i++) {
392 if (seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
393 containsMonitorExit = true;
397 isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter);
402 containsMonitorExit = false;
403 for (Statement st : stats) {
404 containsMonitorExit |= st.isContainsMonitorExit();
408 case TYPE_SYNCHRONIZED:
413 containsMonitorExit = false;
414 for (Statement st : stats) {
415 containsMonitorExit |= st.isContainsMonitorExit();
421 public List<Statement> getReversePostOrderList() {
422 return getReversePostOrderList(first);
425 public List<Statement> getReversePostOrderList(Statement stat) {
426 List<Statement> res = new ArrayList<>();
428 addToReversePostOrderListIterative(stat, res);
433 public List<Statement> getPostReversePostOrderList() {
434 return getPostReversePostOrderList(null);
437 public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) {
439 List<Statement> res = new ArrayList<>();
441 if (lstexits == null) {
442 lstexits = new StrongConnectivityHelper(this).getExitReps();
445 HashSet<Statement> setVisited = new HashSet<>();
447 for (Statement exit : lstexits) {
448 addToPostReversePostOrderList(exit, res, setVisited);
451 if (res.size() != stats.size()) {
452 throw new RuntimeException("computing post reverse post order failed!");
458 public boolean containsStatement(Statement stat) {
459 return this == stat || containsStatementStrict(stat);
462 public boolean containsStatementStrict(Statement stat) {
463 if (stats.contains(stat)) {
467 for (Statement st : stats) {
468 if (st.containsStatementStrict(stat)) {
476 public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
477 throw new RuntimeException("not implemented");
480 // TODO: make obsolete and remove
481 public List<Object> getSequentialObjects() {
482 return new ArrayList<>(stats);
485 public void initExprents() {
489 public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
493 public Statement getSimpleCopy() {
494 throw new RuntimeException("not implemented");
497 public void initSimpleCopy() {
498 if (!stats.isEmpty()) {
499 first = stats.get(0);
503 public void replaceStatement(Statement oldstat, Statement newstat) {
505 for (StatEdge edge : oldstat.getAllPredecessorEdges()) {
506 oldstat.removePredecessor(edge);
507 edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat);
508 newstat.addPredecessor(edge);
511 for (StatEdge edge : oldstat.getAllSuccessorEdges()) {
512 oldstat.removeSuccessor(edge);
513 edge.setSource(newstat);
514 newstat.addSuccessor(edge);
517 int statindex = stats.getIndexByKey(oldstat.id);
518 stats.removeWithKey(oldstat.id);
519 stats.addWithKeyAndIndex(statindex, newstat, newstat.id);
521 newstat.setParent(this);
522 newstat.post = oldstat.post;
524 if (first == oldstat) {
528 List<StatEdge> lst = new ArrayList<>(oldstat.getLabelEdges());
530 for (int i = lst.size() - 1; i >= 0; i--) {
531 StatEdge edge = lst.get(i);
532 if (edge.getSource() != newstat) {
533 newstat.addLabeledEdge(edge);
536 if (this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) {
540 this.addLabeledEdge(edge);
545 oldstat.getLabelEdges().clear();
549 // *****************************************************************************
551 // *****************************************************************************
553 private static void addToReversePostOrderListIterative(Statement root, List<? super Statement> lst) {
555 LinkedList<Statement> stackNode = new LinkedList<>();
556 LinkedList<Integer> stackIndex = new LinkedList<>();
557 HashSet<Statement> setVisited = new HashSet<>();
562 while (!stackNode.isEmpty()) {
564 Statement node = stackNode.getLast();
565 int index = stackIndex.removeLast();
567 setVisited.add(node);
569 List<StatEdge> lstEdges = node.getAllSuccessorEdges();
571 for (; index < lstEdges.size(); index++) {
572 StatEdge edge = lstEdges.get(index);
573 Statement succ = edge.getDestination();
575 if (!setVisited.contains(succ) &&
576 (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter?
578 stackIndex.add(index + 1);
587 if (index == lstEdges.size()) {
590 stackNode.removeLast();
596 private static void addToPostReversePostOrderList(Statement stat, List<? super Statement> lst, HashSet<? super Statement> setVisited) {
598 if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible.
601 setVisited.add(stat);
603 for (StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) {
604 Statement pred = prededge.getSource();
605 if (!setVisited.contains(pred)) {
606 addToPostReversePostOrderList(pred, lst, setVisited);
613 // *****************************************************************************
614 // getter and setter methods
615 // *****************************************************************************
617 public void changeEdgeNode(int direction, StatEdge edge, Statement value) {
619 Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
620 Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
622 int type = edge.getType();
625 if (type == StatEdge.TYPE_EXCEPTION) {
626 arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
629 arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
632 for (int edgetype : arrtypes) {
633 List<StatEdge> lst = mapEdges.get(edgetype);
635 int index = lst.indexOf(edge);
637 mapStates.get(edgetype).set(index, value);
642 if (direction == DIRECTION_BACKWARD) {
643 edge.setSource(value);
646 edge.setDestination(value);
650 public void changeEdgeType(int direction, StatEdge edge, int newtype) {
652 int oldtype = edge.getType();
653 if (oldtype == newtype) {
657 if (oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) {
658 throw new RuntimeException("Invalid edge type!");
661 removeEdgeDirectInternal(direction, edge, oldtype);
662 addEdgeDirectInternal(direction, edge, newtype);
664 if (direction == DIRECTION_FORWARD) {
665 edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype);
668 edge.setType(newtype);
672 private List<StatEdge> getEdges(int type, int direction) {
674 Map<Integer, List<StatEdge>> map = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
677 if ((type & (type - 1)) == 0) {
679 res = res == null ? new ArrayList<>() : new ArrayList<>(res);
682 res = new ArrayList<>();
683 for (int edgetype : StatEdge.TYPES) {
684 if ((type & edgetype) != 0) {
685 List<StatEdge> lst = map.get(edgetype);
696 public List<Statement> getNeighbours(int type, int direction) {
698 Map<Integer, List<Statement>> map = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
701 if ((type & (type - 1)) == 0) {
703 res = res == null ? new ArrayList<>() : new ArrayList<>(res);
706 res = new ArrayList<>();
707 for (int edgetype : StatEdge.TYPES) {
708 if ((type & edgetype) != 0) {
709 List<Statement> lst = map.get(edgetype);
720 public Set<Statement> getNeighboursSet(int type, int direction) {
721 return new HashSet<>(getNeighbours(type, direction));
724 public List<StatEdge> getSuccessorEdges(int type) {
725 return getEdges(type, DIRECTION_FORWARD);
728 public List<StatEdge> getPredecessorEdges(int type) {
729 return getEdges(type, DIRECTION_BACKWARD);
732 public List<StatEdge> getAllSuccessorEdges() {
733 return getEdges(STATEDGE_ALL, DIRECTION_FORWARD);
736 public List<StatEdge> getAllPredecessorEdges() {
737 return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD);
740 public Statement getFirst() {
744 public void setFirst(Statement first) {
748 public Statement getPost() {
752 public VBStyleCollection<Statement, Integer> getStats() {
756 public int getLastBasicType() {
757 return lastBasicType;
760 public HashSet<Statement> getContinueSet() {
764 public boolean isContainsMonitorExit() {
765 return containsMonitorExit;
768 public boolean isMonitorEnter() {
769 return isMonitorEnter;
772 public BasicBlockStatement getBasichead() {
773 if (type == TYPE_BASIC_BLOCK) {
774 return (BasicBlockStatement)this;
777 return first.getBasichead();
781 public boolean isLabeled() {
783 for (StatEdge edge : labelEdges) {
784 if (edge.labeled && edge.explicit) { // FIXME: consistent setting
791 public boolean hasBasicSuccEdge() {
793 // FIXME: default switch
795 return type == TYPE_BASIC_BLOCK || (type == TYPE_IF &&
796 ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) ||
797 (type == TYPE_DO && ((DoStatement)this).getLoopType() != LoopType.DO);
801 public Statement getParent() {
805 public void setParent(Statement parent) {
806 this.parent = parent;
809 public HashSet<StatEdge> getLabelEdges() { // FIXME: why HashSet?
813 public List<Exprent> getVarDefinitions() {
814 return varDefinitions;
817 public List<Exprent> getExprents() {
821 public void setExprents(List<Exprent> exprents) {
822 this.exprents = exprents;
825 public boolean isCopied() {
829 public void setCopied(boolean copied) {
830 this.copied = copied;
834 public String toString() {
835 return id.toString();
838 // *****************************************************************************
839 // IMatchable implementation
840 // *****************************************************************************
843 public IMatchable findObject(MatchNode matchNode, int index) {
844 int node_type = matchNode.getType();
846 if (node_type == MatchNode.MATCHNODE_STATEMENT && !this.stats.isEmpty()) {
847 String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION);
848 if (position != null) {
849 if (position.matches("-?\\d+")) {
850 return this.stats.get((this.stats.size() + Integer.parseInt(position)) % this.stats.size()); // care for negative positions
853 else if (index < this.stats.size()) { // use 'index' parameter
854 return this.stats.get(index);
857 else if (node_type == MatchNode.MATCHNODE_EXPRENT && this.exprents != null && !this.exprents.isEmpty()) {
858 String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);
859 if (position != null) {
860 if (position.matches("-?\\d+")) {
861 return this.exprents.get((this.exprents.size() + Integer.parseInt(position)) % this.exprents.size()); // care for negative positions
864 else if (index < this.exprents.size()) { // use 'index' parameter
865 return this.exprents.get(index);
873 public boolean match(MatchNode matchNode, MatchEngine engine) {
874 if (matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) {
878 for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
879 switch (rule.getKey()) {
881 if (this.type != (Integer)rule.getValue().value) {
885 case STATEMENT_STATSIZE:
886 if (this.stats.size() != (Integer)rule.getValue().value) {
890 case STATEMENT_EXPRSIZE:
891 int exprsize = (Integer)rule.getValue().value;
892 if (exprsize == -1) {
893 if (this.exprents != null) {
898 if (this.exprents == null || this.exprents.size() != exprsize) {
904 if (!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {