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 62b1f8ae6..00772ff04 100644 Binary files a/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf and b/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf differ diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.old b/libsrc/ffdec_lib/testdata/flashdevelop/obj/flashdevelopConfig.old index 758709093..aa763d3a3 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 - '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