diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java index 462444de0..5b3e094e4 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -71,6 +71,149 @@ public class Graph { public static final int SOP_REMOVE_STATIC = 2; + /** + * Identify loop exits + * + * @param localData + * @param N All nodes + * @return + */ + public Map> identifyLoopBreaks(BaseLocalData localData, List N) { + Map> lb = new HashMap<>(); + + for (GraphPart b0 : N) { + List np = new ArrayList<>(b0.nextParts); + np.addAll(b0.throwParts); + for (GraphPart b : np) { + GraphPart hdr = (b0.type == GraphPart.TYPE_LOOP_HEADER || b0.type == GraphPart.TYPE_REENTRY) ? b0 : b0.iloop_header; + + if (hdr != null && b.iloop_header != hdr && b.iloop_header == hdr.iloop_header && b != hdr) { + if (!lb.containsKey(hdr)) { + lb.put(hdr, new ArrayList<>()); + } + if (!lb.get(hdr).contains(b)) { + lb.get(hdr).add(b); + } + } + } + } + + return lb; + } + + /** + * Identifying loops. Based on http://lenx.100871.net/papers/loop-SAS.pdf + * + * @param localData + * @param loopContinues Result - list of loop headers + * @param heads Entries + * @param N All Nodes + */ + public void identifyLoops(BaseLocalData localData, List loopContinues, List heads, List N) { + for (GraphPart b : N) { + b.traversed = false; + b.DFSP_pos = 0; + b.irreducible = false; + b.type = GraphPart.TYPE_NONE; + //initialize b + } + for (GraphPart h0 : heads) { + trav_loops_DFS(localData, loopContinues, h0, 1); + } + } + + /** + * Tag h as loop headr of b. + * + * @param b Block + * @param h Loop header + */ + protected void tag_lhead(GraphPart b, GraphPart h) { + if (b == h || h == null) { + return; + } + GraphPart cur1 = b; + GraphPart cur2 = h; + while (cur1.iloop_header != null) { + GraphPart ih = cur1.iloop_header; + if (ih == cur2) { + return; + } + if (ih.DFSP_pos < cur2.DFSP_pos) { + cur1.iloop_header = cur2; + cur1 = cur2; + cur2 = ih; + } else { + cur1 = h; + } + } + if (cur1 == cur2) { + return; + } + + cur1.iloop_header = cur2; + } + + /** + * Traverse loops deep first search + * + * @param localData + * @param loopHeaders Resulting loop headers + * @param b0 Current node + * @param DFSP_pos Position in DFSP + * @return innermost loop header of b0 + */ + protected GraphPart trav_loops_DFS(BaseLocalData localData, List loopHeaders, GraphPart b0, int DFSP_pos) { + + List folParts = new ArrayList<>(b0.nextParts); + folParts.addAll(b0.throwParts); + + b0.traversed = true; + b0.DFSP_pos = DFSP_pos; //Mark b0’s position in DFSP + for (GraphPart b : folParts) { + b = checkPart(null, localData, b, null); + if (b == null) { + continue; + } + if (!b.traversed) { + //case (A), new + GraphPart nh = trav_loops_DFS(localData, loopHeaders, b, DFSP_pos + 1); + tag_lhead(b0, nh); + } else { + if (b.DFSP_pos > 0) { // b in DFSP(b0) + //case (B) + if (b.type != GraphPart.TYPE_LOOP_HEADER) { + b.type = GraphPart.TYPE_LOOP_HEADER; + loopHeaders.add(b); + } + tag_lhead(b0, b); + } else if (b.iloop_header == null) { + //case (C), do nothing + } else { + GraphPart h = b.iloop_header; + if (h.DFSP_pos > 0) { // h in DFSP(b0) + //case (D) + tag_lhead(b0, h); + } else { // h not in DFSP(b0) + //case (E), reentry + b.type = GraphPart.TYPE_REENTRY; //TODO:and b0,b ? + h.irreducible = true; + while (h.iloop_header != null) { + h = h.iloop_header; + if (h.DFSP_pos > 0) { //h in DFSP(b0) + tag_lhead(b0, h); + break; + } + h.irreducible = true; + } + } + } + } + } + b0.DFSP_pos = 0; // clear b0’s DFSP position + return b0.iloop_header; + } + public Graph(GraphSource code, List alternateEntries) { this.code = code; this.alternateEntries = alternateEntries; @@ -217,6 +360,7 @@ public class Graph { return getCommonPart(localData, part.nextParts, loops); } + //TODO: Make this faster! public GraphPart getCommonPart(BaseLocalData localData, List parts, List loops) throws InterruptedException { if (parts.isEmpty()) { return null; @@ -429,20 +573,46 @@ public class Graph { } TranslateStack stack = new TranslateStack(path); List loops = new ArrayList<>(); - getLoops(localData, heads.get(0), loops, null); + //getLoops(localData, heads.get(0), loops, null); + + List loopHeads = new ArrayList<>(); + identifyLoops(localData, loopHeads, heads, allParts); + Map> loopBreaks = identifyLoopBreaks(localData, allParts); + + List loops2 = new ArrayList<>(); + for (int i = 0; i < loopHeads.size(); i++) { + loops2.add(new Loop(loops2.size(), loopHeads.get(i), null)); + } + for (int i = 0; i < loopHeads.size(); i++) { + if (loopBreaks.containsKey(loopHeads.get(i))) { + /*System.err.print("" + loopHeads.get(i) + ", breaks:"); + for (GraphPart p : loopBreaks.get(loopHeads.get(i))) { + System.err.print("" + p + " "); + } + System.err.println("");*/ + + loops2.get(i).loopBreak = loopBreaks.get(loopHeads.get(i)).get(0);//getMostCommonPart(localData, loopBreaks.get(loopHeads.get(i)), loops2); + } else { + loops2.get(i).loopBreak = null; + } + } + + loops = loops2; /*1 System.err.println(""); for (Loop el : loops) { System.err.println(el); } System.err.println("");*/ + + //TODO: Make getPrecontinues faster getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); + /*System.err.println(""); for (Loop el : loops) { System.err.println(el); } System.err.println("");//*/ - List ret = printGraph(new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); processIfs(ret); finalProcessStack(stack, ret); @@ -831,6 +1001,7 @@ public class Graph { return loopItem; } + //TODO: Make this faster!!! private void getPrecontinues(String path, BaseLocalData localData, GraphPart parent, GraphPart part, List allParts, List loops, List stopPart) throws InterruptedException { try { markLevels(path, localData, part, allParts, loops); @@ -1039,292 +1210,6 @@ public class Graph { } } - private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart) throws InterruptedException { - clearLoops(loops); - getLoops(localData, part, loops, stopPart, true, 1, new ArrayList<>()); - clearLoops(loops); - } - - private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart, boolean first, int level, List visited) throws InterruptedException { - boolean debugMode = false; - - if (stopPart == null) { - stopPart = new ArrayList<>(); - } - if (part == null) { - return; - } - - part = checkPart(null, localData, part, null); - if (part == null) { - return; - } - if (!visited.contains(part)) { - visited.add(part); - } - - if (debugMode) { - System.err.println("getloops: " + part); - } - List loopContinues = getLoopsContinues(loops); - Loop lastP1 = null; - for (Loop el : loops) { - if ((el.phase == 1) && el.loopBreak == null) { //break not found yet - if (el.loopContinue != part) { - lastP1 = el; - - } else { - lastP1 = null; - } - - } - } - if (lastP1 != null) { - if (lastP1.breakCandidates.contains(part)) { - lastP1.breakCandidates.add(part); - lastP1.breakCandidatesLevels.add(level); - return; - } else { - List loopContinues2 = new ArrayList<>(loopContinues); - loopContinues2.remove(lastP1.loopContinue); - List loops2 = new ArrayList<>(loops); - loops2.remove(lastP1); - if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2)) { - if (lastP1.breakCandidatesLocked == 0) { - if (debugMode) { - System.err.println("added breakCandidate " + part + " to " + lastP1); - } - - lastP1.breakCandidates.add(part); - lastP1.breakCandidatesLevels.add(level); - return; - } - } - } - } - - for (Loop el : loops) { - if (el.loopContinue == part) { - return; - } - } - - if (stopPart.contains(part)) { - return; - } - part.level = level; - - boolean isLoop = part.leadsTo(localData, this, code, part, loops); - Loop currentLoop = null; - if (isLoop) { - currentLoop = new Loop(loops.size(), part, null); - currentLoop.phase = 1; - loops.add(currentLoop); - loopContinues.add(part); - } - - if (part.nextParts.size() == 2) { - - List nps = new ArrayList<>(part.nextParts); - /*for(int i=0;i stopPart2 = new ArrayList<>(stopPart); - if (next != null) { - stopPart2.add(next); - } - if (next != nps.get(0)) { - getLoops(localData, nps.get(0), loops, stopPart2, false, level + 1, visited); - } - if (next != nps.get(1)) { - getLoops(localData, nps.get(1), loops, stopPart2, false, level + 1, visited); - } - if (next != null) { - getLoops(localData, next, loops, stopPart, false, level, visited); - } - } - if (part.nextParts.size() > 2) { - GraphPart next = getNextCommonPart(localData, part, loops); - - for (GraphPart p : part.nextParts) { - List stopPart2 = new ArrayList<>(stopPart); - if (next != null) { - stopPart2.add(next); - } - for (GraphPart p2 : part.nextParts) { - if (p2 == p) { - continue; - } - if (!stopPart2.contains(p2)) { - stopPart2.add(p2); - } - } - if (next != p) { - getLoops(localData, p, loops, stopPart2, false, level + 1, visited); - } - } - if (next != null) { - getLoops(localData, next, loops, stopPart, false, level, visited); - } - } - if (part.nextParts.size() == 1) { - getLoops(localData, part.nextParts.get(0), loops, stopPart, false, level, visited); - } - - List loops2 = new ArrayList<>(loops); - for (Loop l : loops2) { - l.breakCandidatesLocked++; - } - for (GraphPart t : part.throwParts) { - if (!visited.contains(t)) { - getLoops(localData, t, loops, stopPart, false, level, visited); - } - } - for (Loop l : loops2) { - l.breakCandidatesLocked--; - } - - if (isLoop) { - GraphPart found; - Map removed = new HashMap<>(); - do { - found = null; - for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { - GraphPart ch = checkPart(null, localData, currentLoop.breakCandidates.get(i), null); - if (ch == null) { - currentLoop.breakCandidates.remove(i); - i--; - } - } - loopcand: - for (GraphPart cand : currentLoop.breakCandidates) { - for (GraphPart cand2 : currentLoop.breakCandidates) { - if (cand == cand2) { - continue; - } - if (cand.leadsTo(localData, this, code, cand2, loops)) { - int lev1 = Integer.MAX_VALUE; - int lev2 = Integer.MAX_VALUE; - for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { - if (currentLoop.breakCandidates.get(i) == cand) { - if (currentLoop.breakCandidatesLevels.get(i) < lev1) { - lev1 = currentLoop.breakCandidatesLevels.get(i); - } - } - if (currentLoop.breakCandidates.get(i) == cand2) { - if (currentLoop.breakCandidatesLevels.get(i) < lev2) { - lev2 = currentLoop.breakCandidatesLevels.get(i); - } - } - } - if (lev1 <= lev2) { - found = cand2; - } else { - found = cand; - } - break loopcand; - } - } - } - if (found != null) { - int maxlevel = 0; - while (currentLoop.breakCandidates.contains(found)) { - int ind = currentLoop.breakCandidates.indexOf(found); - currentLoop.breakCandidates.remove(ind); - int lev = currentLoop.breakCandidatesLevels.remove(ind); - if (lev > maxlevel) { - maxlevel = lev; - } - } - if (removed.containsKey(found)) { - if (removed.get(found) > maxlevel) { - maxlevel = removed.get(found); - } - } - removed.put(found, maxlevel); - } - } while ((found != null) && (currentLoop.breakCandidates.size() > 1)); - - Map count = new HashMap<>(); - GraphPart winner = null; - int winnerCount = 0; - for (GraphPart cand : currentLoop.breakCandidates) { - - if (!count.containsKey(cand)) { - count.put(cand, 0); - } - count.put(cand, count.get(cand) + 1); - boolean otherBreakCandidate = false; - for (Loop el : loops) { - if (el == currentLoop) { - continue; - } - if (el.breakCandidates.contains(cand)) { - otherBreakCandidate = true; - break; - } - } - if (otherBreakCandidate) { - } else if (count.get(cand) > winnerCount) { - winnerCount = count.get(cand); - winner = cand; - } else if (count.get(cand) == winnerCount) { - if (cand.path.length() < winner.path.length()) { - winner = cand; - } - } - } - for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { - GraphPart cand = currentLoop.breakCandidates.get(i); - if (cand != winner) { - int lev = currentLoop.breakCandidatesLevels.get(i); - if (removed.containsKey(cand)) { - if (removed.get(cand) > lev) { - lev = removed.get(cand); - } - } - removed.put(cand, lev); - } - } - currentLoop.loopBreak = winner; - currentLoop.phase = 2; - boolean start = false; - for (int l = 0; l < loops.size(); l++) { - Loop el = loops.get(l); - if (start) { - el.phase = 1; - } - if (el == currentLoop) { - start = true; - } - } - List removedVisited = new ArrayList<>(); - for (GraphPart r : removed.keySet()) { - if (removedVisited.contains(r)) { - continue; - } - getLoops(localData, r, loops, stopPart, false, removed.get(r), visited); - removedVisited.add(r); - } - start = false; - for (int l = 0; l < loops.size(); l++) { - Loop el = loops.get(l); - if (el == currentLoop) { - start = true; - } - if (start) { - el.phase = 2; - } - } - getLoops(localData, currentLoop.loopBreak, loops, stopPart, false, level, visited); - } - } - protected List printGraph(Map> partCodes, Map partCodePos, List visited, BaseLocalData localData, TranslateStack stack, List allParts, GraphPart parent, GraphPart part, List stopPart, List loops, List ret, int staticOperation, String path, int recursionLevel) throws InterruptedException { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java index 1a588fe77..74165eb54 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java @@ -27,6 +27,17 @@ import java.util.List; */ public class GraphPart implements Serializable { + public static final int TYPE_NONE = 0; + public static final int TYPE_LOOP_HEADER = 1; + public static final int TYPE_PRELOOP = 3; + public static final int TYPE_REENTRY = 2; + + public boolean traversed = false; + public int DFSP_pos = 0; + public GraphPart iloop_header; + public int type = TYPE_NONE; + public boolean irreducible = false; + public int start = 0; public int end = 0; @@ -290,7 +301,8 @@ public class GraphPart implements Serializable { @Override public int hashCode() { - int hash = 7; + int hash = 3; + hash = 83 * hash + this.start; return hash; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java index 01e9c2f09..7a0bd4299 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java @@ -32,9 +32,9 @@ public class Loop implements Serializable { public GraphPart loopPreContinue; - public final List breakCandidates = new ArrayList<>(); + public List breakCandidates = new ArrayList<>(); - public final List breakCandidatesLevels = new ArrayList<>(); + public List breakCandidatesLevels = new ArrayList<>(); public long id; @@ -56,4 +56,27 @@ public class Loop implements Serializable { public String toString() { return "loop(id:" + id + (loopPreContinue != null ? ",precontinue:" + loopPreContinue : "") + ",continue:" + loopContinue + ", break:" + loopBreak + ", phase:" + phase + ")"; } + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + (int) (this.id ^ (this.id >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Loop other = (Loop) obj; + if (this.id != other.id) { + return false; + } + return true; + } + }