From 40283273309732a1b04ba92a0b491bb20f5e7714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 19 Jan 2021 18:22:26 +0100 Subject: [PATCH] goto detection... still does not work --- .../flash/abc/avm2/graph/AVM2Graph.java | 35 +- .../flash/abc/types/MethodBody.java | 12 +- .../src/com/jpexs/decompiler/graph/Graph.java | 543 ++++++++++++------ .../decompiler/graph/GraphException.java | 34 ++ .../decompiler/graph/GraphExceptionParts.java | 33 ++ .../com/jpexs/decompiler/graph/GraphPart.java | 6 +- .../decompiler/flash/ActionScript3Test.java | 6 +- .../flashdevelop/bin/flashdevelop.swf | Bin 16115 -> 16375 bytes .../flashdevelop/obj/flashdevelopConfig.old | 2 +- .../flashdevelop/obj/flashdevelopConfig.xml | 2 +- .../testdata/flashdevelop/src/Main.as | 1 + .../flashdevelop/src/tests/TestGotos.as | 43 ++ 12 files changed, 521 insertions(+), 196 deletions(-) create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphException.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphExceptionParts.java create mode 100644 libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos.as diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java index 499f6e9a9..53a6f1b26 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2Graph.java @@ -55,6 +55,7 @@ import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphException; import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphPartEdge; import com.jpexs.decompiler.graph.GraphSource; @@ -72,6 +73,7 @@ import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.LoopItem; import com.jpexs.decompiler.graph.model.NotItem; import com.jpexs.decompiler.graph.model.PopItem; +import com.jpexs.decompiler.graph.model.PushItem; import com.jpexs.decompiler.graph.model.SwitchItem; import com.jpexs.decompiler.graph.model.WhileItem; import java.util.ArrayList; @@ -100,8 +102,17 @@ public class AVM2Graph extends Graph { return avm2code; } + private static List getExceptionEntries(MethodBody body) { + List ret = new ArrayList<>(); + AVM2Code code = body.getCode(); + for (ABCException e : body.exceptions) { + ret.add(new GraphException(code.adr2pos(e.start, true), code.adr2pos(e.end, true), code.adr2pos(e.target))); + } + return ret; + } + public AVM2Graph(AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, List fullyQualifiedNames, HashMap localRegAssigmentIps, HashMap> refs) { - super(new AVM2GraphSource(code, isStatic, scriptIndex, classIndex, localRegs, scopeStack, abc, body, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs), body.getExceptionEntries()); + super(new AVM2GraphSource(code, isStatic, scriptIndex, classIndex, localRegs, scopeStack, abc, body, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs), getExceptionEntries(body)); this.avm2code = code; this.abc = abc; this.body = body; @@ -347,6 +358,28 @@ public class AVM2Graph extends Graph { } List ncatchedCommands = printGraph(foundGotos, gotoTargets, partCodes, partCodePos, localData2, st2, allParts, parent, npart, stopPart2, loops, staticOperation, path); + //hack for findGotos - FIXME + if (hasFinally && !ncatchedCommands.isEmpty()) { + for (int k = 0; k < ncatchedCommands.size(); k++) { + if (ncatchedCommands.get(k) instanceof GotoItem) { + GotoItem gi = (GotoItem) ncatchedCommands.get(k); + for (GotoItem g : foundGotos) { + if (gi.labelName.equals(g.labelName) && g.targetCommands != null) { + if (!g.targetCommands.isEmpty()) { + if (g.targetCommands.get(0) instanceof PushItem) { + if (g.targetCommands.get(0).value instanceof IntegerValueAVM2Item) { + if (((IntegerValueAVM2Item) g.targetCommands.get(0).value).value == -1) { + ncatchedCommands.remove(gi); + } + } + } + } + break; + } + } + } + } + } if (catchedExceptions.get(e).isFinally() && (catchedExceptions.size() > 1 || hasFinally)) { catchedExceptions.remove(e); e--; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java index e225271c1..662861055 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/types/MethodBody.java @@ -37,6 +37,7 @@ import com.jpexs.decompiler.flash.types.annotations.Internal; import com.jpexs.decompiler.flash.types.annotations.SWFField; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphException; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.decompiler.graph.model.LocalData; @@ -157,17 +158,6 @@ public final class MethodBody implements Cloneable { this.codeBytes = null; } - public List getExceptionEntries() { - List ret = new ArrayList<>(); - AVM2Code code = getCode(); - for (ABCException e : exceptions) { - ret.add(code.adr2pos(e.start, true)); - ret.add(code.adr2pos(e.end, true)); - ret.add(code.adr2pos(e.target)); - } - return ret; - } - public void markOffsets() { getCode().markOffsets(); } 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 840ac271e..afa5a9834 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -53,6 +53,7 @@ import com.jpexs.decompiler.graph.model.TernarOpItem; import com.jpexs.decompiler.graph.model.TrueItem; import com.jpexs.decompiler.graph.model.UniversalLoopItem; import com.jpexs.decompiler.graph.model.WhileItem; +import com.jpexs.helpers.Reference; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -76,7 +77,7 @@ public class Graph { protected GraphSource code; - private final List alternateEntries; + private final List exceptions; public static final int SOP_USE_STATIC = 0; @@ -244,9 +245,9 @@ public class Graph { return b0.iloop_header; } - public Graph(GraphSource code, List alternateEntries) { + public Graph(GraphSource code, List exceptions) { this.code = code; - this.alternateEntries = alternateEntries; + this.exceptions = exceptions; } @@ -254,7 +255,7 @@ public class Graph { if (heads != null) { return; } - heads = makeGraph(code, new ArrayList<>(), alternateEntries); + heads = makeGraph(code, new ArrayList<>(), exceptions); int time = 1; List ordered = new ArrayList<>(); List visited = new ArrayList<>(); @@ -617,8 +618,8 @@ public class Graph { return part; } - public static List translateViaGraph(BaseLocalData localData, String path, GraphSource code, List alternateEntries, int staticOperation) throws InterruptedException { - Graph g = new Graph(code, alternateEntries); + public static List translateViaGraph(BaseLocalData localData, String path, GraphSource code, List exceptions, int staticOperation) throws InterruptedException { + Graph g = new Graph(code, exceptions); g.init(localData); return g.translate(localData, staticOperation, path); } @@ -691,12 +692,11 @@ public class Graph { //TODO: Make getPrecontinues faster getBackEdges(localData, loops, new ArrayList<>()); - getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); + //getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); //getPrecontinues2(path, localData, null, heads.get(0), allParts, loops, null); List gotoTargets = new ArrayList<>(); - //TODO: implement and fix this: - //findGotoTargets(path, heads.get(0), loops, gotoTargets); + findGotoTargets(path, heads.get(0), allParts, loops, gotoTargets); /*System.err.println(""); for (Loop el : loops) { @@ -812,16 +812,341 @@ public class Graph { } } - private void findGotoTargets(String path, GraphPart startPart, List loops, List gotoTargets) { - if (!path.equals("classes.tests/ForTest1.test")) { + private void findGotoTargetsWalk(Map> partToNext, Map> partToPrev, Reference currentVirtualNum, List openedExceptions, GraphPartEdge e, + Set opened, + Set closed, + Set closedBranches, + Set exitEdges, + Set backEdges, + Set throwEdges, + Set allowedThrowEdges, + List exceptionParts, + Map> edgeToBranches, + GraphPart start, + List gotoTargets) { + GraphPart p = e.to; + if (closed.contains(p)) { + logger.fine("part " + p + " is already closed, skipping"); + return; + } + logger.fine("processing " + p); + if (!edgeToBranches.containsKey(e)) { + edgeToBranches.put(e, new ArrayList<>()); + } + List branches = edgeToBranches.get(e); + if (p != start) { + List refs = getUnicatePartList(partToPrev.get(p)); + + List> comparedPaths = new ArrayList<>(); + List comparedPathsEdges = new ArrayList<>(); + for (GraphPart r : refs) { + GraphPartEdge re = new GraphPartEdge(r, p); + /*if (throwEdges.contains(re) && !allowedThrowEdges.contains(re)) { + logger.fine("ref edge " + re + " is throwedge, ignored"); + continue; + }*/ + if (backEdges.contains(re)) { + logger.fine("ref edge " + re + " is backedge, ignored"); + continue; + } + if (r.start == -1) { + logger.fine("ref edge " + re + " is alternatestart, ignored"); + continue; + } + if (!edgeToBranches.containsKey(re)) { + //edge not yet processed + logger.fine("ref edge " + re + " NOT yet processed"); + return; + } else { + logger.fine("ref edge " + re + " already processed"); + } + comparedPaths.add(edgeToBranches.get(re)); + comparedPathsEdges.add(re); + + } + + boolean isEndOfBlock = false; + + 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 (comparedPaths.get(i).isEmpty()) { + //logger.info("path isempty, removing it "); + comparedPaths.remove(i); + comparedPathsEdges.remove(i); + i--; + continue loopi; + } + 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) { + int removedCount = 0; + for (int k = comparedPaths.size() - 1; k >= 0; k--) { + if (k == i) { + continue; + } + if (comparedPaths.get(i).equals(comparedPaths.get(k))) { + comparedPaths.remove(k); + comparedPathsEdges.remove(k); + removedCount++; + } + if (removedCount == partToNext.get(decision).size() - 1) { + break; + } + } + comparedPaths.get(i).remove(comparedPaths.get(i).size() - 1); + } else { + //remove last path component + int last = i > j ? i : j; + int first = i < j ? i : j; + comparedPaths.get(first).remove(comparedPaths.get(first).size() - 1); + comparedPaths.remove(last); + comparedPathsEdges.remove(last); + } + //logger.fine("normal closing " + decision); + + if (!closedBranches.contains(decision)) { + logger.fine("on part " + p); + logger.fine("normal closing branch " + decision); + closedBranches.add(decision); + } else { + logger.fine("branch already closed: " + decision); + isEndOfBlock = true; + } + closedBranches.add(decision); + i = -1; + continue loopi; + } + } + } + for (List cp : comparedPaths) { + logger.fine("- branches:" + pathToString(cp)); + } + + if (comparedPaths.size() > 1) { + logger.fine("not a single path"); + for (int i = 0; i < comparedPaths.size(); i++) { + + } + List prefix = getCommonPrefix(comparedPaths); + Set partsToClose = new HashSet<>(); + GraphPart decision = prefix.isEmpty() ? null : prefix.get(prefix.size() - 1); + + for (int i = 0; i < comparedPaths.size(); i++) { + for (int j = prefix.size() - 1; j < comparedPaths.get(i).size(); j++) { + if (j < 0) { + continue; + } + GraphPart partToClose = comparedPaths.get(i).get(j); + GraphPartEdge edgeToClose = comparedPathsEdges.get(i); + if (!closedBranches.contains(partToClose)) { + logger.fine("on part " + p); + logger.fine("closing branch " + partToClose); + partsToClose.add(partToClose); + } else { + logger.fine("branch already closed: " + partToClose); + logger.fine("probably break edge: " + edgeToClose); + isEndOfBlock = true; + } + } + } + /*if (decision != null) { + logger.fine("closing branch 2: " + decision); + closedBranches.add(decision); + }*/ + closedBranches.addAll(partsToClose); + branches = prefix; + if (!branches.isEmpty()) { + branches.remove(branches.size() - 1); + } + + } + + if (isEndOfBlock) { + //GraphPart blockStartPart = getDominator(startPart, p, loops); + //logger.info("found breaks to to " + p); + //System.err.println("found breaks to to " + p); + for (GraphPart r : p.refs) { + gotoTargets.add(new GraphPartEdge(r, p)); + } + //gotoTargets.add(new GraphPartEdge(e.from, e.to)); + } + + } + + List nexts = new ArrayList<>(); + + boolean isExceptionStart = false; + GraphExceptionParts nearestEx = null; + List currentExceptions = new ArrayList<>(); + List currentExceptionTargets = new ArrayList<>(); + + for (GraphExceptionParts ex : exceptionParts) { + if (openedExceptions.contains(ex)) { + continue; + } + if (p.equals(ex.start)) { + if (nearestEx != null && !ex.end.equals(nearestEx.end)) { + continue; + } + isExceptionStart = true; + currentExceptions.add(ex); + nearestEx = ex; + currentExceptionTargets.add(ex.target); + } + } + + if (!currentExceptionTargets.isEmpty()) { + //openedExceptions = new ArrayList<>(); + openedExceptions.addAll(currentExceptions); + List virtualNexts = new ArrayList<>(); + virtualNexts.add(p); + virtualNexts.addAll(currentExceptionTargets); + virtualNexts.add(nearestEx.end); + currentVirtualNum.setVal(currentVirtualNum.getVal() - 1); + GraphPart virtualPart = new GraphPart(currentVirtualNum.getVal(), currentVirtualNum.getVal()); + partToNext.put(virtualPart, virtualNexts); + partToPrev.put(virtualPart, new ArrayList<>()); + + // connection from prevs of p to virtualPart + 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++) { + if (prevNexts.get(j).equals(p)) { + prevNexts.set(j, virtualPart); + } + } + partToPrev.get(virtualPart).add(pr); + } + for (GraphPart t : virtualNexts) { + if (t == nearestEx.end) { + partToPrev.get(t).add(virtualPart); + } else { + partToPrev.get(t).clear(); + partToPrev.get(t).add(virtualPart); + } + } + p = virtualPart; + } + + //filter out backedges + for (GraphPart n : partToNext.get(p)) { + GraphPartEdge ne = new GraphPartEdge(p, n); + if (!backEdges.contains(ne)) { + nexts.add(n); + } else { + logger.fine("next edge " + ne + " is backedge, ignored"); + } + } + + + int sizeNextsNoThrow = nexts.size(); + + closed.add(p); + + Stack walkStack = new Stack<>(); + logger.fine("processing nextparts of " + p); + for (GraphPart n : nexts) { + GraphPartEdge ne = new GraphPartEdge(p, n); + List subBranches = branches; + if (nexts.size() > 1) { + subBranches = new ArrayList<>(branches); + subBranches.add(p); + } + edgeToBranches.put(ne, subBranches); + walkStack.push(ne); + } + + /*if (isTryBegin(p)) { + for (GraphPart t : p.throwParts) { + GraphPartEdge te = new GraphPartEdge(p, t); + if (backEdges.contains(te)) { + continue; + } + edgeToBranches.put(te, branches); + walkStack.push(te); + } + }*/ + + /*logger.fine("processing throwparts of " + p); + for (GraphPart n : p.throwParts) { + GraphPartEdge ne = new GraphPartEdge(p, n); + List subBranches = new ArrayList<>(branches); + if (p.throwParts.size() > 1) { + subBranches = new ArrayList<>(branches); + subBranches.add(p); + } + edgeToBranches.put(ne, subBranches); + walkStack.push(ne); + }*/ + while (!walkStack.isEmpty()) { + findGotoTargetsWalk(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) { + if (!path.endsWith(".run")) { //return; } + //logger.info("------ " + path); //logger.info("GETTING precontinues of " + path + " ================="); Set opened = new HashSet<>(); Set closed = new HashSet<>(); Set closedBranches = new HashSet<>(); Set exitEdges = new HashSet<>(); Set backEdges = new HashSet<>(); + Set throwEdges = new HashSet<>(); + Set allowedThrowEdges = new HashSet<>(); + List exceptionParts = new ArrayList<>(); + Map partByIp = new HashMap<>(); + Map> partToNext = new HashMap<>(); + Map> partToPrev = new HashMap<>(); + for (GraphPart p : allParts) { + if (p.start == -1) { + continue; + } + //if (p.start >= 0) { + partByIp.put(p.start, p); + //} + partToNext.put(p, new ArrayList<>()); + partToNext.get(p).addAll(p.nextParts); + partToPrev.put(p, new ArrayList<>()); + + for (GraphPart r : p.refs) { + if (r.start != -1) { + partToPrev.get(p).add(r); + } + } + } + + for (GraphException ex : exceptions) { + exceptionParts.add(new GraphExceptionParts( + partByIp.containsKey(ex.start) ? partByIp.get(ex.start) : null, + partByIp.containsKey(ex.end) ? partByIp.get(ex.end) : null, + partByIp.containsKey(ex.target) ? partByIp.get(ex.target) : null + )); + } + + for (GraphPart p : allParts) { + for (GraphPart tp : p.throwParts) { + GraphPartEdge e = new GraphPartEdge(p, tp); + if (!throwEdges.contains(e)) { + throwEdges.add(e); + logger.fine("throwpart " + e); + if (isTryBegin(p)) { + allowedThrowEdges.add(e); + } + } + } + } + for (Loop el : loops) { for (GraphPart g : el.backEdges) { backEdges.add(new GraphPartEdge(g, el.loopContinue)); @@ -830,172 +1155,16 @@ public class Graph { Map> edgeToBranches = new HashMap<>(); opened.add(startPart); GraphPart start = startPart; - Stack walkStack = new Stack<>(); - walkStack.push(new GraphPartEdge(startPart, startPart)); - loopwalk: - while (!walkStack.isEmpty()) { - GraphPartEdge e = walkStack.pop(); - GraphPart p = e.to; - if (closed.contains(p)) { - logger.fine("part " + p + " is already closed, skipping"); - continue; + findGotoTargetsWalk(partToNext, partToPrev, new Reference<>(-2), new ArrayList<>(), new GraphPartEdge(startPart, startPart), opened, closed, closedBranches, exitEdges, backEdges, throwEdges, allowedThrowEdges, exceptionParts, edgeToBranches, start, gotoTargets); + } + + private boolean isTryBegin(GraphPart part) { + for (GraphPart r : part.refs) { + if (r.start == -1) { + return true; } - logger.fine("processing " + p); - if (!edgeToBranches.containsKey(e)) { - edgeToBranches.put(e, new ArrayList<>()); - } - List branches = edgeToBranches.get(e); - if (p != start) { - List refs = getUnicatePartList(p.refs); - - List> comparedPaths = new ArrayList<>(); - List comparedPathsEdges = new ArrayList<>(); - for (GraphPart r : refs) { - GraphPartEdge re = new GraphPartEdge(r, p); - if (backEdges.contains(re)) { - logger.fine("ref edge " + re + " is backedge, ignored"); - continue; - } - if (r.start == -1) { - logger.fine("ref edge " + re + " is alternatestart, ignored"); - continue; - } - if (!edgeToBranches.containsKey(re)) { - //edge not yet processed - logger.fine("ref edge " + re + " NOT yet processed"); - continue loopwalk; - } else { - logger.fine("ref edge " + re + " already processed"); - } - comparedPaths.add(edgeToBranches.get(re)); - comparedPathsEdges.add(re); - - } - 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 (comparedPaths.get(i).isEmpty()) { - //logger.info("path isempty, removing it "); - comparedPaths.remove(i); - comparedPathsEdges.remove(i); - i--; - continue loopi; - } - logger.fine("merged paths:" + pathToString(comparedPaths.get(i))); - - - GraphPart decision = comparedPaths.get(i).get(comparedPaths.get(i).size() - 1); - if (decision.nextParts.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))) { - comparedPaths.remove(k); - comparedPathsEdges.remove(k); - removedCount++; - } - if (removedCount == decision.nextParts.size() - 1) { - break; - } - } - comparedPaths.get(i).remove(comparedPaths.get(i).size() - 1); - } else { - //remove last path component - int last = i > j ? i : j; - int first = i < j ? i : j; - comparedPaths.get(first).remove(comparedPaths.get(first).size() - 1); - comparedPaths.remove(last); - comparedPathsEdges.remove(last); - } - i = -1; - continue loopi; - } - } - } - for (List cp : comparedPaths) { - logger.fine("- branches:" + pathToString(cp)); - } - if (comparedPaths.size() > 1) { - logger.fine("not a single path"); - List prefix = getCommonPrefix(comparedPaths); - Set partsToClose = new HashSet<>(); - boolean isEndOfBlock = false; - 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); - GraphPartEdge edgeToClose = comparedPathsEdges.get(i); - if (!closedBranches.contains(partToClose)) { - logger.fine("on part " + p); - logger.fine("closing branch " + partToClose); - partsToClose.add(partToClose); - } else { - logger.fine("probably break edge: " + edgeToClose); - isEndOfBlock = true; - } - } - } - closedBranches.addAll(partsToClose); - branches = prefix; - if (!branches.isEmpty()) { - branches.remove(branches.size() - 1); - } - - if (isEndOfBlock) { - //GraphPart blockStartPart = getDominator(startPart, p, loops); - logger.fine("found breaks to to " + p); - for (GraphPart r : p.refs) { - gotoTargets.add(new GraphPartEdge(r, p)); - } - //gotoTargets.add(new GraphPartEdge(e.from, e.to)); - } - - } - - } - - //filter out backedges - List nexts = new ArrayList<>(); - for (GraphPart n : p.nextParts) { - GraphPartEdge ne = new GraphPartEdge(p, n); - if (!backEdges.contains(ne)) { - nexts.add(n); - } else { - logger.fine("next edge " + ne + " is backedge, ignored"); - } - } - - int sizeNextsNoThrow = nexts.size(); - for (GraphPart t : p.throwParts) { - GraphPartEdge te = new GraphPartEdge(p, t); - if (!backEdges.contains(te)) { - nexts.add(t); - } else { - logger.fine("throw edge " + te + " is backedge, ignored"); - } - } - - closed.add(p); - - logger.fine("processing nextparts of " + p); - for (GraphPart n : nexts) { - GraphPartEdge ne = new GraphPartEdge(p, n); - List subBranches = branches; - if (sizeNextsNoThrow > 1) { - subBranches = new ArrayList<>(branches); - subBranches.add(p); - } - edgeToBranches.put(ne, subBranches); - walkStack.push(ne); - } - } + return false; } private void getPrecontinues2(String path, BaseLocalData localData, GraphPart parent, GraphPart part, Set allParts, List loops, List stopPart) throws InterruptedException { @@ -1036,6 +1205,18 @@ public class Graph { return result; } + private List getUnicateRefsNoThrow(GraphPart part, Set throwEdges) { + List result = new ArrayList<>(); + for (GraphPart r : part.refs) { + GraphPartEdge edge = new GraphPartEdge(r, part); + if (!throwEdges.contains(edge)) { + result.add(r); + } + } + + return getUnicatePartList(result); + } + private List getUsableRefs(GraphPart g, List loops) { List ret = getUnicatePartList(g.refs); for (Loop el : loops) { @@ -2716,7 +2897,7 @@ public class Graph { List finalComm = new ArrayList<>(); //findGotoTargets - comment this out: - if (currentLoop.loopPreContinue != null) { + /*if (currentLoop.loopPreContinue != null) { GraphPart backup = currentLoop.loopPreContinue; currentLoop.loopPreContinue = null; List stopPart2 = new ArrayList<>(stopPart); @@ -2724,7 +2905,7 @@ public class Graph { finalComm = printGraph(foundGotos, gotoTargets, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1); currentLoop.loopPreContinue = backup; checkContinueAtTheEnd(finalComm, currentLoop); - } + }*/ if (currentLoop.precontinueCommands != null) { finalComm.addAll(currentLoop.precontinueCommands); } @@ -2857,7 +3038,13 @@ public class Graph { protected void checkGraph(List allBlocks) { } - public List makeGraph(GraphSource code, List allBlocks, List alternateEntries) throws InterruptedException { + public List makeGraph(GraphSource code, List allBlocks, List exceptions) throws InterruptedException { + List alternateEntries = new ArrayList<>(); + for (GraphException ex : exceptions) { + alternateEntries.add(ex.start); + alternateEntries.add(ex.end); + alternateEntries.add(ex.target); + } HashMap> refs = code.visitCode(alternateEntries); List ret = new ArrayList<>(); boolean[] visited = new boolean[code.size()]; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphException.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphException.java new file mode 100644 index 000000000..da1056780 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphException.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +public class GraphException { + + public int start; + public int end; + public int target; + + public GraphException(int start, int end, int target) { + this.start = start; + this.end = end; + this.target = target; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphExceptionParts.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphExceptionParts.java new file mode 100644 index 000000000..3b1aa55b7 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphExceptionParts.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * + * @author JPEXS + */ +public class GraphExceptionParts { + public GraphPart start; + public GraphPart end; + public GraphPart target; + + public GraphExceptionParts(GraphPart start, GraphPart end, GraphPart target) { + this.start = start; + this.end = end; + this.target = target; + } +} 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 21055bafc..59ab4e9c9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java @@ -266,7 +266,11 @@ public class GraphPart implements Serializable { if (end < start) { return "<-> " + (start + 1) + "-" + (end + 1); } - return "" + (start + 1) + "-" + (end + 1) + (instanceCount > 1 ? "(" + instanceCount + " links)" : "");// + " p" + path; + int printStart = start + 1; + int printEnd = end + 1; + + return "" + (printStart < 0 ? "(" : "") + printStart + (printStart < 0 ? ")" : "") + + "-" + (printEnd < 0 ? "(" : "") + printEnd + (printEnd < 0 ? ")" : "") + (instanceCount > 1 ? "(" + instanceCount + " links)" : "");// + " p" + path; } public boolean containsIP(int ip) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java index 6a709d04a..4cfd1ff46 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -413,7 +413,7 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } - //@Test FIXME + @Test public void testForAnd() { decompileMethod("testForAnd", "var x:Boolean = false;\r\n" + "var len:int = 5;\r\n" @@ -525,7 +525,7 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } - //@Test FIXME + @Test public void testForGoto() { decompileMethod("testForGoto", "var c:int = 0;\r\n" + "var len:int = 5;\r\n" @@ -565,7 +565,7 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } - //@Test FIXME + @Test public void testForXml() { decompileMethod("testForXml", "var c:int = 0;\r\n" + "var name:String = \"ahoj\";\r\n" diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf b/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf index 62b1f8ae63af4fb9dd8151523fc3b751dc9f16cd..00772ff04469a138aeec63da48d52498af5caee0 100644 GIT binary patch literal 16375 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 CONFIG::timeStamp - '18.01.2021' + '19.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 758709093..aa763d3a3 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 - '18.01.2021' + '19.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 cd5c728f1..ea46ac615 100644 --- a/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as @@ -36,6 +36,7 @@ package TestForGoto; TestForIn; TestForXml; + TestGotos; TestHello; TestIf; TestIfElse; diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos.as new file mode 100644 index 000000000..32db399b3 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos.as @@ -0,0 +1,43 @@ +package tests +{ + + public class TestGotos + { + + public final function run(param1:Object):int + { + var _loc2_:Boolean = true; + var _loc3_:Boolean = false; + var _loc4_:Boolean = false; + + if (_loc2_) + { + trace("A"); + } + else if (_loc3_) + { + trace("B"); + + } + else + { + try + { + if (_loc2_) + { + return 7; + } + trace("x"); + } + catch (e:Error) + { + trace("z"); + } + } + + return 89; + } + + } + +} \ No newline at end of file