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 1b28e3363..100f70ad0 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 @@ -659,7 +659,7 @@ public class AVM2Graph extends Graph { } GraphPart breakPart = getMostCommonPart(localData, caseBodyParts, loops, new ArrayList<>()); - removeEdgeToFromList(gotoTargets, breakPart); + //removeEdgeToFromList(gotoTargets, breakPart); List> caseCommands = new ArrayList<>(); GraphPart next = breakPart; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java index e4a975e75..1d5248dc7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraph.java @@ -467,7 +467,7 @@ public class ActionGraph extends Graph { } GraphPart breakPart = getMostCommonPart(localData, caseBodyParts, loops, new ArrayList<>()); - removeEdgeToFromList(gotoTargets, breakPart); + //removeEdgeToFromList(gotoTargets, breakPart); List> caseCommands = new ArrayList<>(); GraphPart next = breakPart; @@ -482,7 +482,7 @@ public class ActionGraph extends Graph { for (int i = 0; i < caseValuesMap.size(); i++) { GraphPart cur = caseBodyParts.get(i); if (!caseBodies.contains(cur)) { - removeEdgeToFromList(gotoTargets, cur); + //removeEdgeToFromList(gotoTargets, cur); caseBodies.add(cur); } valuesMapping.add(caseBodies.indexOf(cur)); 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 9ee04d74a..6e261b927 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -542,6 +542,9 @@ public class Graph { System.err.println("");//*/ List gotos = new ArrayList<>(); List ret = printGraph(gotos, gotoTargets, new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); + + processIfGotos(gotos, ret); + Map usages = new HashMap<>(); Map lastUsage = new HashMap<>(); for (GotoItem gi : gotos) { @@ -552,10 +555,12 @@ public class Graph { lastUsage.put(gi.labelName, gi); } for (String labelName : usages.keySet()) { + logger.fine("usage - " + labelName + ": " + usages.get(labelName)); if (usages.get(labelName) == 1) { lastUsage.get(labelName).labelName = null; } } + expandGotos(ret); processIfs(ret); finalProcessStack(stack, ret, path); @@ -735,6 +740,7 @@ public class Graph { } logger.fine("current branches: " + System.identityHashCode(branches) + ": " + pathToString(branches)); + List foundBreakEdges = new ArrayList<>(); if (comparedPaths.size() > 1) { logger.fine("not a single path - paths left: " + comparedPaths.size()); List prefix = getCommonPrefix(comparedPaths); @@ -754,6 +760,7 @@ public class Graph { logger.fine("branch already closed: " + partToClose); logger.fine("probably break edge: " + edgeToClose); if (ignoredBreakEdges.contains(edgeToClose)) { + foundBreakEdges.add(edgeToClose); logger.fine("NOT a break edge, it is standard break"); continue; } @@ -788,6 +795,10 @@ public class Graph { for (GraphPart r : p.refs) { gotoTargets.add(new GraphPartEdge(r, p)); } + /*for (GraphPartEdge be : foundBreakEdges) { + GraphPartEdge re = findGotoRestoreOrigEdge(be); + gotoTargets.add(re); + }*/ } } @@ -849,6 +860,24 @@ public class Graph { } } + /*private GraphPartEdge findGotoRestoreOrigEdge(GraphPartEdge e) { + for (GraphPart r : e.to.refs) { + if (r.equals(e.from)) { + return e; + } + } + for (GraphPart r : e.to.refs) { + GraphPart rr = r; + while (isPartEmpty(rr) && rr.refs.size() == 1) { + rr = rr.refs.get(0); + } + if (rr.equals(e.from)) { + return new GraphPartEdge(r, e.to); + } + } + return null; + }*/ + protected boolean isPartEmpty(GraphPart part) { return false; } @@ -1278,6 +1307,58 @@ public class Graph { } } + /** + * if (xxx) { y ; goto a } else { z ; goto a } + * + * => + * + * if (xxx) { y } else { z } goto a + * + */ + private void processIfGotos(List allGotos, List list) { + for (int i = 0; i < list.size(); i++) { + GraphTargetItem item = list.get(i); + if (item instanceof Block) { + List> subs = ((Block) item).getSubs(); + for (List sub : subs) { + processIfGotos(allGotos, sub); + } + } + if (item instanceof IfItem) { + IfItem ii = (IfItem) item; + if (!ii.onTrue.isEmpty() && !ii.onFalse.isEmpty()) { + if (ii.onTrue.get(ii.onTrue.size() - 1) instanceof GotoItem) { + if (ii.onFalse.get(ii.onFalse.size() - 1) instanceof GotoItem) { + GotoItem gotoOnTrue = (GotoItem) ii.onTrue.get(ii.onTrue.size() - 1); + GotoItem gotoOnFalse = (GotoItem) ii.onFalse.get(ii.onFalse.size() - 1); + if (gotoOnTrue.labelName.equals(gotoOnFalse.labelName)) { + String labelOnTrue = gotoOnTrue.labelName; + String labelOnFalse = gotoOnFalse.labelName; + if (labelOnTrue != null && labelOnFalse != null) { + if (labelOnTrue.equals(labelOnFalse)) { + GotoItem gotoMerged; + GotoItem gotoRemoved; + if (gotoOnTrue.targetCommands != null) { + gotoMerged = gotoOnTrue; + gotoRemoved = gotoOnFalse; + } else { + gotoMerged = gotoOnFalse; + gotoRemoved = gotoOnTrue; + } + ii.onTrue.remove(ii.onTrue.size() - 1); + ii.onFalse.remove(ii.onFalse.size() - 1); + list.add(i + 1, gotoMerged); + allGotos.remove(gotoRemoved); + } + } + } + } + } + } + } + } + } + private void processIfs(List list) { for (int i = 0; i < list.size(); i++) { GraphTargetItem item = list.get(i); @@ -1297,25 +1378,6 @@ public class Graph { IfItem ifi = (IfItem) item; List onTrue = ifi.onTrue; List onFalse = ifi.onFalse; - if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { - if (onTrue.get(onTrue.size() - 1) instanceof GotoItem) { - if (onFalse.get(onFalse.size() - 1) instanceof GotoItem) { - GotoItem gotoOnTrue = (GotoItem) onTrue.get(onTrue.size() - 1); - GotoItem gotoOnFalse = (GotoItem) onFalse.get(onFalse.size() - 1); - String labelOnTrue = gotoOnTrue.labelName; - String labelOnFalse = gotoOnFalse.labelName; - if (labelOnTrue != null && labelOnFalse != null) { - if (labelOnTrue.equals(labelOnFalse)) { - GotoItem gotoMerged = gotoOnTrue.targetCommands != null ? gotoOnTrue : gotoOnFalse; - - onTrue.remove(onTrue.size() - 1); - onFalse.remove(onFalse.size() - 1); - list.add(i + 1, gotoMerged); - } - } - } - } - } if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) { if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) { if (onFalse.get(onFalse.size() - 1) instanceof ContinueItem) { @@ -2195,7 +2257,7 @@ public class Graph { gi.targetCommands.remove(cnt); l.precontinueCommands = gi.targetCommands; l.loopPreContinue = part; - removeEdgeToFromList(gotoTargets, part); + //removeEdgeToFromList(gotoTargets, part); ret.add(cnt); return ret; } @@ -2291,7 +2353,7 @@ public class Graph { List> caseCommands = new ArrayList<>(); List valueMappings = new ArrayList<>(); Loop swLoop = new Loop(loops.size(), null, next); - gotoTargets.remove(next); + //removeEdgeToFromList(gotoTargets, next); swLoop.phase = 1; loops.add(swLoop); boolean first = false; @@ -2383,7 +2445,7 @@ public class Graph { pos = 0; List nextCommands = new ArrayList<>(); for (int i = 1; i < part.nextParts.size(); i++) { - gotoTargets.remove(part.nextParts.get(i)); + //gotoTargets.remove(new GraphPartEdge(next, part.nextParts.get(i))); } for (int i = 1; i < part.nextParts.size(); i++) { GraphPart p = part.nextParts.get(i); @@ -3100,5 +3162,9 @@ public class Graph { edges.remove(i); } } + while (isPartEmpty(to) && !to.nextParts.isEmpty()) { + to = to.nextParts.get(0); + removeEdgeToFromList(edges, to); + } } } 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 1083b7d88..6cb3f954b 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -31,16 +31,11 @@ import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.decompiler.graph.ScopeStack; import java.io.BufferedInputStream; -import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; @@ -729,6 +724,60 @@ public class ActionScript3Test extends ActionScriptTestBase { false); } + @Test + public void testGotos6() { + decompileMethod("testGotos6", "var a:Boolean = true;\r\n" + + "var s:String = \"a\";\r\n" + + "if(a)\r\n" + + "{\r\n" + + "switch(s)\r\n" + + "{\r\n" + + "case \"a\":\r\n" + + "trace(\"is A\");\r\n" + + "break;\r\n" + + "case \"b\":\r\n" + + "trace(\"is B\");\r\n" + + "case \"c\":\r\n" + + "trace(\"is BC\");\r\n" + + "}\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"D\");\r\n" + + "}\r\n" + + "trace(\"finish\");\r\n", + false); + } + + @Test + public void testGotos7() { + decompileMethod("testGotos7", "for(var i:int = 0; i < 10; i++)\r\n" + + "{\r\n" + + "switch(i)\r\n" + + "{\r\n" + + "case 0:\r\n" + + "trace(\"zero\");\r\n" + + "continue;\r\n" + + "case 5:\r\n" + + "trace(\"five\");\r\n" + + "break;\r\n" + + "case 10:\r\n" + + "trace(\"ten\");\r\n" + + "break;\r\n" + + "case 1:\r\n" + + "if(i == 7)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"one\");\r\n" + + "default:\r\n" + + "trace(\"def\");\r\n" + + "}\r\n" + + "trace(\"before loop end\");\r\n" + + "}\r\n", + false); + } + @Test public void testHello() { decompileMethod("testHello", "trace(\"hello\");\r\n", diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf b/libsrc/ffdec_lib/testdata/flashdevelop/bin/flashdevelop.swf index 970e70d62..b5034f1bb 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/src/Main.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as index bc80273ff..b406df078 100644 --- a/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/Main.as @@ -41,6 +41,8 @@ package TestGotos3; TestGotos4; TestGotos5; + TestGotos6; + TestGotos7; TestHello; TestIf; TestIfElse; diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos6.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos6.as new file mode 100644 index 000000000..5b8935f3e --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos6.as @@ -0,0 +1,35 @@ +package tests +{ + + public class TestGotos6 + { + + public function run():void + { + var a:Boolean = true; + var s:String = "a"; + + if (a) + { + switch (s) + { + case "a": + trace("is A"); + break; + case "b": + trace("is B"); + case "c": + trace("is BC"); + break; + } + } + else + { + trace("D"); + } + trace("finish"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos7.as b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos7.as new file mode 100644 index 000000000..0ed14381b --- /dev/null +++ b/libsrc/ffdec_lib/testdata/flashdevelop/src/tests/TestGotos7.as @@ -0,0 +1,38 @@ +package tests +{ + + public class TestGotos7 + { + + public function run():void + { + + for (var i:int = 0; i < 10; i++) + { + switch (i) + { + case 0: + trace("zero"); + continue; + case 5: + trace("five"); + break; + case 10: + trace("ten"); + break; + case 1: + if (i == 7) + { + continue; + } + trace("one"); + default: + trace("def"); + } + trace("before loop end"); + } + } + + } + +} \ No newline at end of file