From 22c2206aec3094659da48c823c7cb36885e64954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 22 Jan 2021 20:46:44 +0100 Subject: [PATCH] Fixed goto/for detection --- .../src/com/jpexs/decompiler/graph/Graph.java | 502 +++++------------- .../decompiler/graph/GraphPartDecision.java | 68 +++ .../flashdevelop/bin/flashdevelop.swf | Bin 16704 -> 17212 bytes .../flashdevelop/obj/flashdevelopConfig.old | 2 +- .../flashdevelop/obj/flashdevelopConfig.xml | 2 +- .../testdata/flashdevelop/src/Main.as | 3 + .../flashdevelop/src/tests/TestGotos3.as | 33 ++ .../flashdevelop/src/tests/TestGotos4.as | 28 + .../flashdevelop/src/tests/TestGotos5.as | 32 ++ 9 files changed, 288 insertions(+), 382 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPartDecision.java create mode 100644 libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos3.as create mode 100644 libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos4.as create mode 100644 libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos5.as 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 068537d15..4daba1129 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -100,151 +100,11 @@ public class Graph { return new LinkedHashMap<>(); } - /** - * Identify loop exits - * - * @param localData - * @param allParts All nodes - * @return - */ - public Map> identifyLoopBreaks(BaseLocalData localData, Set allParts) { - Map> lb = new HashMap<>(); - - for (GraphPart b0 : allParts) { - 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 appParts All Nodes - */ - public void identifyLoops(BaseLocalData localData, List loopContinues, List heads, Set appParts) { - for (GraphPart b : appParts) { - 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; - } protected List filter(List list) { return new ArrayList<>(list); } - /** - * 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 exceptions) { this.code = code; this.exceptions = exceptions; @@ -657,30 +517,7 @@ public class Graph { TranslateStack stack = new TranslateStack(path); List loops = new ArrayList<>(); - //TODO: Make this working. :-( - final boolean newLoopDetection = false; - - if (!newLoopDetection) { - getLoops(localData, heads.get(0), loops, null); - } else { - 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))) { - 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; - } + getLoops(localData, heads.get(0), loops, null); if (debugPrintLoopList) { System.err.println(""); @@ -696,7 +533,7 @@ public class Graph { //getPrecontinues2(path, localData, null, heads.get(0), allParts, loops, null); List gotoTargets = new ArrayList<>(); - findGotoTargets(path, heads.get(0), allParts, loops, gotoTargets); + findGotoTargets(localData, path, heads.get(0), allParts, loops, gotoTargets); /*System.err.println(""); for (Loop el : loops) { @@ -726,8 +563,8 @@ public class Graph { return ret; } - private List getCommonPrefix(List> listOfLists) { - List result = new ArrayList<>(); + private List getCommonPrefix(List> listOfLists) { + List result = new ArrayList<>(); if (listOfLists.isEmpty()) { return result; } @@ -739,7 +576,7 @@ public class Graph { } } for (int i = 0; i < maxlen; i++) { - List firstList = listOfLists.get(0); + List firstList = listOfLists.get(0); for (int j = 1; j < listOfLists.size(); j++) { if (!listOfLists.get(j).get(i).equals(firstList.get(i))) { return result; @@ -750,69 +587,28 @@ public class Graph { return result; } - private GraphPart getDominator(GraphPart graphStart, GraphPart targetPart, List loops) { - Set refs = new LinkedHashSet<>(); - List parts = targetPart.refs; - - List> allPredecessorsLists = new ArrayList<>(); - for (GraphPart part : parts) { - List allPredecessors = new ArrayList<>(); - getAllPredecessors(part, allPredecessors, loops, null); - //allRefs.addAll(allPredecessors); - logger.info("predecessors of part " + part + " are: " + pathToString(allPredecessors)); - allPredecessorsLists.add(allPredecessors); + private boolean isDecisionJoin(List list1, List list2) { + if (list1.size() != list2.size()) { + return false; } - List firstList = allPredecessorsLists.get(0); - GraphPart commonPart = null; - looppart: - for (GraphPart part : firstList) { - for (int i = 1; i < allPredecessorsLists.size(); i++) { - if (!allPredecessorsLists.get(i).contains(part)) { - continue looppart; - } - } - logger.info("checking predeccessor " + part); - for (GraphPart part2 : parts) { - List allPredecessors = new ArrayList<>(); - getAllPredecessors(part2, allPredecessors, loops, part); - logger.info("predecessors of " + part2 + ": " + allPredecessors); - if (allPredecessors.contains(graphStart)) { - logger.info("graphstart(" + graphStart + ") found, " + part + " will not be correct"); - continue looppart; - } - } - - commonPart = part; - - break; + if (list1.isEmpty()) { + return false; } - - return commonPart; + for (int i = 0; i < list1.size() - 1; i++) { + if (!list1.get(i).equals(list2.get(i))) { + return false; + } + } + if (!list1.get(list1.size() - 1).part.equals(list2.get(list2.size() - 1).part)) { + return false; + } + if (list1.get(list1.size() - 1).way == list2.get(list2.size() - 1).way) { + return false; + } + return true; } - private void getAllPredecessors(GraphPart part, List ret, List loops, GraphPart ignored) { - List toprocess = new ArrayList<>(); - loopref: - for (GraphPart ref : part.refs) { - for (Loop el : loops) { - if (part.equals(el.loopContinue) && el.backEdges.contains(ref)) { - continue loopref; - } - } - if (ref == ignored) { - continue; - } - if (!ret.contains(ref)) { - ret.add(0, ref); - toprocess.add(ref); - } - } - for (GraphPart ref : toprocess) { - getAllPredecessors(ref, ret, loops, ignored); - } - } - - private void findGotoTargetsWalk(Map partReplacements, Map> partToNext, Map> partToPrev, Reference currentVirtualNum, List openedExceptions, GraphPartEdge e, + private void findGotoTargetsWalk(Set ignoredBreakEdges, Map partReplacements, Map> partToNext, Map> partToPrev, Reference currentVirtualNum, List openedExceptions, GraphPartEdge e, Set opened, Set closed, Set closedBranches, @@ -821,7 +617,7 @@ public class Graph { Set throwEdges, Set allowedThrowEdges, List exceptionParts, - Map> edgeToBranches, + Map> edgeToBranches, GraphPart start, List gotoTargets) { GraphPart p = e.to; @@ -833,11 +629,11 @@ public class Graph { if (!edgeToBranches.containsKey(e)) { edgeToBranches.put(e, new ArrayList<>()); } - List branches = edgeToBranches.get(e); + List branches = edgeToBranches.get(e); if (p != start) { List refs = getUnicatePartList(partToPrev.get(p)); - List> comparedPaths = new ArrayList<>(); + List> comparedPaths = new ArrayList<>(); List comparedPathsEdges = new ArrayList<>(); for (GraphPart r : refs) { GraphPartEdge re = new GraphPartEdge(r, p); @@ -875,17 +671,15 @@ public class Graph { Set partsToClose = new HashSet<>(); - Map decisionToRemovedCount = new HashMap<>(); + //Map decisionToRemovedCount = new HashMap<>(); loopi: for (int i = 0; i < comparedPaths.size(); i++) { for (int j = 0; j < comparedPaths.size(); j++) { if (i == j) { continue; } - if (comparedPaths.get(i).equals(comparedPaths.get(j))) { - //logger.info("paths match: " + comparedPaths.get(i)); + if (isDecisionJoin(comparedPaths.get(i), comparedPaths.get(j))) { if (comparedPaths.get(i).isEmpty()) { - //logger.info("path isempty, removing it "); comparedPaths.remove(i); comparedPathsEdges.remove(i); i--; @@ -893,23 +687,23 @@ public class Graph { } logger.fine("merged paths:" + pathToString(comparedPaths.get(i)) + " of edge " + comparedPathsEdges.get(i)); - GraphPart decision = comparedPaths.get(i).get(comparedPaths.get(i).size() - 1); - if (partToNext.get(decision).size() > 2) { + GraphPartDecision decision = comparedPaths.get(i).get(comparedPaths.get(i).size() - 1); + if (partToNext.get(decision.part).size() > 2) { int removedCount = 0; for (int k = comparedPaths.size() - 1; k >= 0; k--) { if (k == i) { continue; } - if (comparedPaths.get(i).equals(comparedPaths.get(k))) { + if (isDecisionJoin(comparedPaths.get(i), comparedPaths.get(k))) { comparedPaths.remove(k); comparedPathsEdges.remove(k); removedCount++; } - if (removedCount == partToNext.get(decision).size() - 1) { + if (removedCount == partToNext.get(decision.part).size() - 1) { break; } } - decisionToRemovedCount.put(decision, removedCount); + //decisionToRemovedCount.put(decision, removedCount); comparedPaths.get(i).remove(comparedPaths.get(i).size() - 1); } else { //remove last path component @@ -922,35 +716,35 @@ public class Graph { } //logger.fine("normal closing " + decision); - if (!closedBranches.contains(decision)) { + if (!closedBranches.contains(decision.part)) { logger.fine("on part " + p); logger.fine("normal closing branch " + decision); - partsToClose.add(decision); + partsToClose.add(decision.part); } else { logger.fine("branch already closed: " + decision); isEndOfBlock = true; } - partsToClose.add(decision); + partsToClose.add(decision.part); i = -1; continue loopi; } } } - for (List cp : comparedPaths) { + for (List cp : comparedPaths) { logger.fine("- branches: " + System.identityHashCode(cp) + ": " + pathToString(cp)); } logger.fine("current branches: " + System.identityHashCode(branches) + ": " + pathToString(branches)); if (comparedPaths.size() > 1) { logger.fine("not a single path - paths left: " + comparedPaths.size()); - List prefix = getCommonPrefix(comparedPaths); + List prefix = getCommonPrefix(comparedPaths); - GraphPart decision = prefix.isEmpty() ? null : prefix.get(prefix.size() - 1); - int removedCount = decisionToRemovedCount.containsKey(decision) ? decisionToRemovedCount.get(decision) : 0; + //GraphPartDecision decision = prefix.isEmpty() ? null : prefix.get(prefix.size() - 1); + //int removedCount = decisionToRemovedCount.containsKey(decision) ? decisionToRemovedCount.get(decision) : 0; for (int i = 0; i < comparedPaths.size(); i++) { for (int j = prefix.size(); j < comparedPaths.get(i).size(); j++) { - GraphPart partToClose = comparedPaths.get(i).get(j); + GraphPart partToClose = comparedPaths.get(i).get(j).part; GraphPartEdge edgeToClose = comparedPathsEdges.get(i); if (!closedBranches.contains(partToClose)) { logger.fine("on part " + p); @@ -959,6 +753,10 @@ public class Graph { } else { logger.fine("branch already closed: " + partToClose); logger.fine("probably break edge: " + edgeToClose); + if (ignoredBreakEdges.contains(edgeToClose)) { + logger.fine("NOT a break edge, it is standard break"); + continue; + } isEndOfBlock = true; } } @@ -970,8 +768,8 @@ public class Graph { }*/ branches = prefix; if (!branches.isEmpty()) { - logger.fine("removedCount before: " + removedCount); - removedCount += comparedPaths.size(); + //logger.fine("removedCount before: " + removedCount); + //removedCount += comparedPaths.size(); /*if (partToNext.get(decision).size() > 2 && removedCount < partToNext.get(decision).size() - 1) { //ignore } else {*/ @@ -986,18 +784,13 @@ public class Graph { closedBranches.addAll(partsToClose); if (isEndOfBlock) { - //GraphPart blockStartPart = getDominator(startPart, p, loops); - logger.info("found breaks to " + p); - //System.err.println("found breaks to to " + p); + logger.fine("found breaks to " + p); for (GraphPart r : p.refs) { gotoTargets.add(new GraphPartEdge(r, p)); } - //gotoTargets.add(new GraphPartEdge(e.from, e.to)); } - } - boolean isExceptionStart = false; GraphExceptionParts nearestEx = null; List currentExceptions = new ArrayList<>(); List currentExceptionTargets = new ArrayList<>(); @@ -1010,7 +803,6 @@ public class Graph { if (nearestEx != null && !ex.end.equals(nearestEx.end)) { continue; } - isExceptionStart = true; currentExceptions.add(ex); nearestEx = ex; currentExceptionTargets.add(ex.target); @@ -1018,10 +810,9 @@ public class Graph { } if (!currentExceptionTargets.isEmpty()) { - //openedExceptions = new ArrayList<>(); openedExceptions.addAll(currentExceptions); List virtualNexts = new ArrayList<>(); - //virtualNexts.add(p); + virtualNexts.add(p); virtualNexts.addAll(currentExceptionTargets); GraphPart end = nearestEx.end; if (partReplacements.containsKey(end)) { @@ -1034,7 +825,7 @@ public class Graph { partToPrev.put(virtualPart, new ArrayList<>()); // connection from prevs of p to virtualPart - /*for (int k = 0; k < partToPrev.get(p).size(); k++) { + for (int k = 0; k < partToPrev.get(p).size(); k++) { GraphPart pr = partToPrev.get(p).get(k); List prevNexts = partToNext.get(pr); for (int j = 0; j < prevNexts.size(); j++) { @@ -1043,7 +834,7 @@ public class Graph { } } partToPrev.get(virtualPart).add(pr); - }*/ + } for (GraphPart t : virtualNexts) { if (t == end) { partToPrev.get(t).add(virtualPart); @@ -1052,17 +843,17 @@ public class Graph { partToPrev.get(t).add(virtualPart); } } - findGotoWalkNexts(partReplacements, partToNext, partToPrev, currentVirtualNum, openedExceptions, opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets, virtualPart, branches); + findGotoWalkNexts(ignoredBreakEdges, partReplacements, partToNext, partToPrev, currentVirtualNum, openedExceptions, opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets, virtualPart, branches); + } else { + findGotoWalkNexts(ignoredBreakEdges, partReplacements, partToNext, partToPrev, currentVirtualNum, openedExceptions, opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets, p, branches); } - - findGotoWalkNexts(partReplacements, partToNext, partToPrev, currentVirtualNum, openedExceptions, opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets, p, branches); } protected boolean isPartEmpty(GraphPart part) { return false; } - private void findGotoWalkNexts(Map partReplacements, Map> partToNext, Map> partToPrev, Reference currentVirtualNum, List openedExceptions, + private void findGotoWalkNexts(Set ignoredBreakEdges, Map partReplacements, Map> partToNext, Map> partToPrev, Reference currentVirtualNum, List openedExceptions, Set opened, Set closed, Set closedBranches, @@ -1071,11 +862,11 @@ public class Graph { Set throwEdges, Set allowedThrowEdges, List exceptionParts, - Map> edgeToBranches, + Map> edgeToBranches, GraphPart start, List gotoTargets, GraphPart p, - List branches) { + List branches) { List nexts = new ArrayList<>(); //filter out backedges @@ -1092,23 +883,24 @@ public class Graph { Stack walkStack = new Stack<>(); logger.fine("processing nextparts of " + p); - for (GraphPart n : nexts) { + for (int i = 0; i < nexts.size(); i++) { + GraphPart n = nexts.get(i); GraphPartEdge ne = new GraphPartEdge(p, n); - List subBranches = branches; + List subBranches = branches; if (nexts.size() > 1) { subBranches = new ArrayList<>(branches); - subBranches.add(p); + subBranches.add(new GraphPartDecision(p, i)); } edgeToBranches.put(ne, subBranches); walkStack.push(ne); } while (!walkStack.isEmpty()) { - findGotoTargetsWalk(partReplacements, partToNext, partToPrev, currentVirtualNum, openedExceptions, walkStack.pop(), opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets); + findGotoTargetsWalk(ignoredBreakEdges, partReplacements, partToNext, partToPrev, currentVirtualNum, openedExceptions, walkStack.pop(), opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets); } } - private void findGotoTargets(String path, GraphPart startPart, Set allParts, List loops, List gotoTargets) { + private void findGotoTargets(BaseLocalData localData, String path, GraphPart startPart, Set allParts, List loops, List gotoTargets) throws InterruptedException { if (!path.endsWith(".run")) { //return; } @@ -1165,6 +957,36 @@ public class Graph { } } + for (Loop el : loops) { + for (GraphPart g : el.backEdges) { + backEdges.add(new GraphPartEdge(g, el.loopContinue)); + } + } + + Set ignoredBreakEdges = new HashSet<>(); + + for (Loop el : loops) { + el.phase = 2; //? + } + for (Loop el : loops) { + if (el.loopBreak != null && el.loopContinue != null) { + GraphPart br = el.loopBreak; + GraphPart brRepl = partReplacements.containsKey(br) ? partReplacements.get(br) : br; + GraphPart cntRepl = partReplacements.containsKey(el.loopContinue) ? partReplacements.get(el.loopContinue) : el.loopContinue; + for (GraphPart p : partToPrev.get(brRepl)) { + GraphPartEdge e = new GraphPartEdge(p, brRepl); + logger.fine("continue " + cntRepl + " leads to " + p + " ?"); + if (cntRepl.equals(p) || findGotoTargetsLeadsTo(cntRepl, p, partToNext, backEdges, new HashSet<>())) { + ignoredBreakEdges.add(e); + logger.fine("YES: ingored break edge: " + e); + } else { + logger.fine("NO: NOT A BREAK EDGE: " + e); + } + } + } + } + clearLoops(loops); + for (GraphException ex : exceptions) { exceptionParts.add(new GraphExceptionParts( partByIp.containsKey(ex.start) ? partByIp.get(ex.start) : null, @@ -1186,15 +1008,34 @@ public class Graph { } } - for (Loop el : loops) { - for (GraphPart g : el.backEdges) { - backEdges.add(new GraphPartEdge(g, el.loopContinue)); - } - } - Map> edgeToBranches = new HashMap<>(); + Map> edgeToBranches = new HashMap<>(); opened.add(startPart); GraphPart start = startPart; - findGotoTargetsWalk(partReplacements, partToNext, partToPrev, new Reference<>(-2), new ArrayList<>(), new GraphPartEdge(startPart, startPart), opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets); + findGotoTargetsWalk(ignoredBreakEdges, partReplacements, partToNext, partToPrev, new Reference<>(-2), new ArrayList<>(), new GraphPartEdge(startPart, startPart), opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets); + } + + private boolean findGotoTargetsLeadsTo( + GraphPart part, + GraphPart target, + Map> partToNext, + Set backEdges, Set visited) { + visited.add(part); + for (GraphPart n : partToNext.get(part)) { + if (visited.contains(n)) { + continue; + } + GraphPartEdge e = new GraphPartEdge(part, n); + if (backEdges.contains(e)) { + continue; + } + if (n.equals(target)) { + return true; + } + if (findGotoTargetsLeadsTo(n, target, partToNext, backEdges, visited)) { + return true; + } + } + return false; } private boolean isTryBegin(GraphPart part) { @@ -1206,29 +1047,9 @@ public class Graph { return false; } - private void getPrecontinues2(String path, BaseLocalData localData, GraphPart parent, GraphPart part, Set allParts, List loops, List stopPart) throws InterruptedException { - for (Loop el : loops) { - if (el.backEdges.size() == 1) { //for statement has single real continue - GraphPart backEdge = (GraphPart) el.backEdges.toArray()[0]; - - GraphPart g = backEdge; - Set openedParts = new HashSet(); - openedParts.addAll(g.nextParts); - List level0Parts = new ArrayList(); - getPrecontinues2Walk(new HashMap(), level0Parts, new HashMap>>(), openedParts, g, loops, el, 0, new HashMap>(), new ArrayList<>()); - if (!level0Parts.isEmpty()) { - GraphPart lastPart = level0Parts.get(level0Parts.size() - 1); - el.loopPreContinue = lastPart; - logger.info("Found precontinue: " + lastPart); - } - - } - } - } - - private String pathToString(Collection list) { + private String pathToString(Collection list) { List strs = new ArrayList<>(); - for (GraphPart p : list) { + for (Object p : list) { strs.add(p.toString()); } return "[" + String.join(", ", strs) + "]"; @@ -1268,83 +1089,6 @@ public class Graph { return ret; } - private void getPrecontinues2Walk(Map numRefsRemaining, List level0Parts, Map>> joinedPaths, Set openedParts, GraphPart g, List loops, Loop currentLoop, int partId, Map> partPaths, List path) { - logger.fine("start walk " + g); - if (g == currentLoop.loopContinue) { - return; - } - if (openedParts.contains(g)) { - logger.fine("- already walked - terminate"); - return; //already walked - } - - if (!joinedPaths.containsKey(g)) { - joinedPaths.put(g, new ArrayList<>()); - } - joinedPaths.get(g).add(path); - - boolean checkNext = true; - for (Loop el : loops) { - if (el.loopContinue == g) { - checkNext = false; - logger.fine("do not check next as current is loopcontinue"); - break; - } - } - logger.fine("opened parts: " + pathToString(openedParts)); - - if (checkNext) { - //Do not proceed up before handling all descendants - for (GraphPart next : g.nextParts) { - if (!openedParts.contains(next)) { - logger.fine("- waiting for next descendats - terminate"); - return; - } - } - } - - List jp1 = joinedPaths.get(g).get(0); - for (List jp : joinedPaths.get(g)) { - if (!jp.equals(jp1)) { - //paths in both branches do not match, do not continue up - logger.fine("- Paths mismatch - terminate"); - logger.fine("path " + pathToString(jp) + " does not match " + pathToString(jp1)); - return; - } - } - - if (!path.isEmpty() && g.nextParts.size() > 1) { - GraphPart lastPathComponent = path.get(path.size() - 1); - logger.fine("numrefs before = " + numRefsRemaining.get(lastPathComponent)); - numRefsRemaining.put(lastPathComponent, numRefsRemaining.get(lastPathComponent) - 1); - logger.fine("numrefs = " + numRefsRemaining.get(lastPathComponent)); - if (numRefsRemaining.get(lastPathComponent) == 1) { - logger.fine("numrefs is zero, removing last item of path " + pathToString(path)); - path.remove(path.size() - 1); - } - } - if (path.isEmpty()) { - logger.fine("path is empty, adding current item"); - level0Parts.add(g); - } - - logger.fine("added to openedParts"); - openedParts.add(g); - - List refs = getUsableRefs(g, loops); - numRefsRemaining.put(g, refs.size()); - logger.fine("setting numrefs to " + refs.size()); - logger.fine("all refs = " + pathToString(refs)); - for (GraphPart ref : refs) { - logger.fine("going to ref " + ref + "..."); - List subPath = new ArrayList(path); - if (refs.size() > 1) { - subPath.add(g); - } - getPrecontinues2Walk(numRefsRemaining, level0Parts, joinedPaths, openedParts, ref, loops, currentLoop, partId, partPaths, subPath); - } - logger.fine("/All refs of " + g + " processed"); - } /**/ //if (ref.nextParts) @@ -2059,8 +1803,6 @@ public class Graph { 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, new ArrayList<>())) { @@ -2108,7 +1850,7 @@ public class Graph { }*/ nps = part.nextParts; - GraphPart next = getCommonPart(localData, nps, loops, new ArrayList<>());//part.getNextPartPath(loopContinues); + GraphPart next = getCommonPart(localData, nps, loops, new ArrayList<>()); List stopPart2 = stopPart == null ? new ArrayList<>() : new ArrayList<>(stopPart); if (next != null) { stopPart2.add(next); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPartDecision.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPartDecision.java new file mode 100644 index 000000000..790288a53 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPartDecision.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010-2018 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.graph; + +import java.util.Objects; + +/** + * + * @author JPEXS + */ +public class GraphPartDecision { + public GraphPart part; + public int way; + + public GraphPartDecision(GraphPart part, int way) { + this.part = part; + this.way = way; + } + + @Override + public String toString() { + return part.toString() + ":" + way; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 59 * hash + Objects.hashCode(this.part); + hash = 59 * hash + this.way; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final GraphPartDecision other = (GraphPartDecision) obj; + if (this.way != other.way) { + return false; + } + if (!Objects.equals(this.part, other.part)) { + return false; + } + return true; + } + +} diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf b/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf index dac9336b85d5d3e7d38ff4535b687c482b164725..4cdfdfcc3a771213000b5677f07ce24464003f51 100644 GIT binary patch literal 17212 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 P#q*Wtu=9Tbi{^8_Ox+zG literal 16704 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 diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.old b/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.old index aa763d3a3..b52fa2342 100644 --- a/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.old +++ b/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.old @@ -16,7 +16,7 @@ CONFIG::timeStamp - '19.01.2021' + '22.01.2021' CONFIG::air diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.xml b/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.xml index f0f38ee1c..b52fa2342 100644 --- a/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.xml +++ b/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.xml @@ -16,7 +16,7 @@ CONFIG::timeStamp - '20.01.2021' + '22.01.2021' CONFIG::air diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as index ad36c3dc0..bc80273ff 100644 --- a/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as @@ -38,6 +38,9 @@ package TestForXml; TestGotos; TestGotos2; + TestGotos3; + TestGotos4; + TestGotos5; TestHello; TestIf; TestIfElse; diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos3.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos3.as new file mode 100644 index 000000000..1e40309c5 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos3.as @@ -0,0 +1,33 @@ +package tests +{ + public class TestGotos3 + { + + public function run() : void + { + var a:int = 5; + if (a > 5) + { + for (var i:int = 0; i < 5; i++) + { + if (i > 3) + { + trace("A"); + if (i == 4) + { + break; + } + } + trace("B"); + } + } + else + { + trace("C"); + } + trace("return"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos4.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos4.as new file mode 100644 index 000000000..b18e26ed9 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos4.as @@ -0,0 +1,28 @@ +package tests +{ + public class TestGotos4 + { + + public function run() : void + { + var a:int = 5; + if(a > 3) + { + if(a < 7) + { + try + { + trace("A"); + } + catch(error:Error) + { + } + trace("B"); + } + } + trace("return"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos5.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos5.as new file mode 100644 index 000000000..f98f41c85 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos5.as @@ -0,0 +1,32 @@ +package tests +{ + + public class TestGotos5 + { + + public function run() : void + { + var s:String = "A"; + var i:int = 0; + for(; i < 10; i++) + { + if(s == "B") + { + if(s == "C") + { + continue; + } + } + trace("D"); + var j:int = 0; + while(j < 29) + { + trace("E"); + j++; + } + } + } + + } + +} \ No newline at end of file