diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1698ea8..a903a3c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Accessing font list on demand - prevents loading X11 on systems without UI - Better AS2 class detection +- AS1/2 break statement decompilation in for..in loops - AS2 direct editation - not generating Pop in class header ifs ## [11.0.0] - 2018-01-17 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 ce146e4f5..07436c060 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 @@ -456,15 +456,15 @@ public class ActionGraph extends Graph { @Override protected int checkIp(int ip) { int oldIp = ip; - //return in for..in + //return/break in for..in GraphSourceItem action = code.get(ip); if ((action instanceof ActionPush) && (((ActionPush) action).values.size() == 1) && (((ActionPush) action).values.get(0) == Null.INSTANCE)) { - if (ip + 4 < code.size()) { + if (ip + 3 <= code.size()) { if ((code.get(ip + 1) instanceof ActionEquals) || (code.get(ip + 1) instanceof ActionEquals2)) { if (code.get(ip + 2) instanceof ActionNot) { if (code.get(ip + 3) instanceof ActionIf) { ActionIf aif = (ActionIf) code.get(ip + 3); - if (code.adr2pos(code.pos2adr(ip + 4) + aif.getJumpOffset()) == ip) { + if (code.adr2pos(code.pos2adr(ip + 3) + 5 /*IF numbytes*/ + aif.getJumpOffset()) == ip) { ip += 4; } } @@ -473,6 +473,9 @@ public class ActionGraph extends Graph { } } if (oldIp != ip) { + if (ip == code.size()) { //no next checkIp call since its after code size + return ip; + } return checkIp(ip); } return ip; 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 0347d2c0c..a28f7b585 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -75,6 +75,11 @@ public class Graph { public static final int SOP_REMOVE_STATIC = 2; + private boolean debugPrintAllParts = false; + private boolean debugPrintLoopList = false; + private boolean debugGetLoops = false; + private boolean debugPrintGraph = false; + /** * Identify loop exits * @@ -595,10 +600,30 @@ public class Graph { } public List translate(BaseLocalData localData, int staticOperation, String path) throws InterruptedException { + Set allParts = new HashSet<>(); for (GraphPart head : heads) { populateParts(head, allParts); } + if (debugPrintAllParts) { + System.err.println("parts:"); + for (GraphPart p : allParts) { + System.err.print(p); + if (!p.nextParts.isEmpty()) { + System.err.print(", next: "); + } + boolean first = true; + for (GraphPart n : p.nextParts) { + if (!first) { + System.err.print(","); + } + System.err.print(n); + first = false; + } + System.err.println(""); + } + System.err.println("/parts"); + } TranslateStack stack = new TranslateStack(path); List loops = new ArrayList<>(); @@ -627,13 +652,14 @@ public class Graph { loops = loops2; } - /* - System.err.println(""); - for (Loop el : loops) { - System.err.println(el); - } - System.err.println(""); - */ + if (debugPrintLoopList) { + System.err.println(""); + for (Loop el : loops) { + System.err.println(el); + } + System.err.println(""); + } + //TODO: Make getPrecontinues faster getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null); @@ -1102,7 +1128,6 @@ public class Graph { } private void markLevels(String path, BaseLocalData localData, GraphPart part, Set allParts, List loops, List stopPart, int level, Set visited, int recursionLevel) throws InterruptedException { - boolean debugMode = false; if (stopPart == null) { stopPart = new ArrayList<>(); } @@ -1110,9 +1135,6 @@ public class Graph { throw new RuntimeException(path + ": markLevels max recursion level reached"); } - if (debugMode) { - System.err.println("markLevels " + part); - } if (stopPart.contains(part)) { return; } @@ -1121,9 +1143,6 @@ public class Graph { return; } if (el.phase != 1) { - if (debugMode) { - //System.err.println("ignoring "+el); - } continue; } if (el.loopContinue == part) { @@ -1252,7 +1271,6 @@ public class Graph { } private void getLoops(BaseLocalData localData, GraphPart part, List loops, List stopPart, boolean first, int level, List visited) throws InterruptedException { - boolean debugMode = false; if (part == null) { return; @@ -1266,7 +1284,7 @@ public class Graph { visited.add(part); } - if (debugMode) { + if (debugGetLoops) { System.err.println("getloops: " + part); } //List loopContinues = getLoopsContinues(loops); @@ -1294,7 +1312,7 @@ public class Graph { loops2.remove(lastP1); if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2)) { if (lastP1.breakCandidatesLocked == 0) { - if (debugMode) { + if (debugGetLoops) { System.err.println("added breakCandidate " + part + " to " + lastP1); } @@ -1424,6 +1442,7 @@ public class Graph { } } } + // if (lev1 <= lev2) { found = cand2; } else { @@ -1542,9 +1561,8 @@ public class Graph { ret = new ArrayList<>(); } //try { - boolean debugMode = false; - if (debugMode) { + if (debugPrintGraph) { System.err.println("PART " + part + " nextsize:" + part.nextParts.size()); } @@ -1584,19 +1602,19 @@ public class Graph { } } - if (debugMode) { + if (debugPrintGraph) { System.err.println("loopsize:" + loops.size()); } for (int l = loops.size() - 1; l >= 0; l--) { Loop el = loops.get(l); if (el == currentLoop) { - if (debugMode) { + if (debugPrintGraph) { System.err.println("ignoring current loop " + el); } continue; } if (el.phase != 1) { - if (debugMode) { + if (debugPrintGraph) { System.err.println("ignoring loop " + el); } continue; @@ -1605,7 +1623,7 @@ public class Graph { if (currentLoop != null) { currentLoop.phase = 0; } - if (debugMode) { + if (debugPrintGraph) { System.err.println("Adding break"); } ret.add(new BreakItem(null, localData.lineStartInstruction, el.id)); @@ -1615,7 +1633,7 @@ public class Graph { if (currentLoop != null) { currentLoop.phase = 0; } - if (debugMode) { + if (debugPrintGraph) { System.err.println("Adding precontinue"); } ret.add(new ContinueItem(null, localData.lineStartInstruction, el.id)); @@ -1625,7 +1643,7 @@ public class Graph { if (currentLoop != null) { currentLoop.phase = 0; } - if (debugMode) { + if (debugPrintGraph) { System.err.println("Adding continue"); } ret.add(new ContinueItem(null, localData.lineStartInstruction, el.id)); @@ -1637,7 +1655,7 @@ public class Graph { if (currentLoop != null) { currentLoop.phase = 0; } - if (debugMode) { + if (debugPrintGraph) { System.err.println("Stopped on part " + part); } return ret; @@ -2275,6 +2293,10 @@ public class Graph { ret.path = path; GraphPart part = ret; while (ip < code.size()) { + ip = checkIp(ip); + if (ip >= code.size()) { + break; + } if (visited2[ip] || ((ip != startip) && (refs.get(ip).size() > 1))) { part.end = lastIp; GraphPart found = null; @@ -2299,8 +2321,6 @@ public class Graph { part = gp; } } - - ip = checkIp(ip); lastIp = ip; GraphSourceItem ins = code.get(ip); if (ins.isIgnored()) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java index b9f91d020..3bc7e66e3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSource.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.graph; import com.jpexs.decompiler.flash.BaseLocalData; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java index b6d66c9c6..ec1637b32 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java @@ -916,4 +916,114 @@ public class ActionScript2Test extends ActionScript2TestBase { + "tst.includeTests();\r\n" ); } + + @Test + public void frame66_forInBreakTest() { + compareSrc(66, "function testFunc1()\r\n" + + "{\r\n" + + "for(var _loc1_ in obj)\r\n" + + "{\r\n" + + "trace(_loc1_);\r\n" + + "}\r\n" + + "}\r\n" + + "function testFunc2()\r\n" + + "{\r\n" + + "for(var _loc1_ in obj)\r\n" + + "{\r\n" + + "if(_loc1_ == \"b\")\r\n" + + "{\r\n" + + "trace(\"found\");\r\n" + + "break;\r\n" + + "}\r\n" + + "}\r\n" + + "}\r\n" + + "function testFunc3()\r\n" + + "{\r\n" + + "for(var _loc1_ in obj)\r\n" + + "{\r\n" + + "if(_loc1_ == \"b\")\r\n" + + "{\r\n" + + "trace(\"hi\");\r\n" + + "break;\r\n" + + "}\r\n" + + "}\r\n" + + "trace(\"after\");\r\n" + + "}\r\n" + + "function testFunc4()\r\n" + + "{\r\n" + + "for(var _loc1_ in obj)\r\n" + + "{\r\n" + + "if(_loc1_ == \"b\")\r\n" + + "{\r\n" + + "trace(\"hi\");\r\n" + + "}\r\n" + + "else if(_loc1_ == \"c\")\r\n" + + "{\r\n" + + "trace(\"hello\");\r\n" + + "}\r\n" + + "else\r\n" + + "{\r\n" + + "trace(\"hohoho\");\r\n" + + "continue;\r\n" + + "}\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"after\");\r\n" + + "}\r\n" + + "function testFunc5()\r\n" + + "{\r\n" + + "var _loc1_ = {key1:1,key2:2,key3:3};\r\n" + + "for(var _loc3_ in obj)\r\n" + + "{\r\n" + + "if(_loc3_ == \"a\")\r\n" + + "{\r\n" + + "trace(\"loop1_break\");\r\n" + + "break;\r\n" + + "}\r\n" + + "if(_loc3_ == \"b\")\r\n" + + "{\r\n" + + "trace(\"hello\");\r\n" + + "for(var _loc2_ in _loc1_)\r\n" + + "{\r\n" + + "if(_loc2_ == \"key1\")\r\n" + + "{\r\n" + + "trace(\"loop2_break\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"loop2_inside\");\r\n" + + "}\r\n" + + "trace(\"after_loop2\");\r\n" + + "}\r\n" + + "trace(\"loop1_inside\");\r\n" + + "}\r\n" + + "trace(\"after_loop1\");\r\n" + + "}\r\n" + + "function testFunc6()\r\n" + + "{\r\n" + + "var _loc1_ = {key1:1,key2:2,key3:3};\r\n" + + "for(var _loc3_ in obj)\r\n" + + "{\r\n" + + "if(_loc3_ == \"a\")\r\n" + + "{\r\n" + + "trace(\"loop1_break\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"hello\");\r\n" + + "for(var _loc2_ in _loc1_)\r\n" + + "{\r\n" + + "if(_loc2_ == \"key1\")\r\n" + + "{\r\n" + + "trace(\"loop2_break\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"loop2_inside\");\r\n" + + "}\r\n" + + "trace(\"after_loop2\");\r\n" + + "}\r\n" + + "trace(\"after_loop1\");\r\n" + + "}\r\n" + + "trace(\"forInBreakTest\");\r\n" + + "var obj = {a:5,b:6,c:7};\r\n" + ); + } } diff --git a/libsrc/ffdec_lib/testdata/as2/as2.fla b/libsrc/ffdec_lib/testdata/as2/as2.fla index c0a1918ef..623e55c81 100644 Binary files a/libsrc/ffdec_lib/testdata/as2/as2.fla and b/libsrc/ffdec_lib/testdata/as2/as2.fla differ diff --git a/libsrc/ffdec_lib/testdata/as2/as2.swf b/libsrc/ffdec_lib/testdata/as2/as2.swf index 043554b4a..3c6b3afbb 100644 Binary files a/libsrc/ffdec_lib/testdata/as2/as2.swf and b/libsrc/ffdec_lib/testdata/as2/as2.swf differ diff --git a/libsrc/ffdec_lib/testdata/as2/com/jpexs/flash/test/TestLoader.as b/libsrc/ffdec_lib/testdata/as2/com/jpexs/flash/test/TestLoader.as index 3058fb789..4f12ac264 100644 --- a/libsrc/ffdec_lib/testdata/as2/com/jpexs/flash/test/TestLoader.as +++ b/libsrc/ffdec_lib/testdata/as2/com/jpexs/flash/test/TestLoader.as @@ -1,12 +1,12 @@ -/** +/** * Class for including other TestCases, it is called from frame 65 */ class com.jpexs.flash.test.TestLoader { - + public function includeTests() { - new com.jpexs.flash.test.testcases.TestSetterGetter(); - new com.jpexs.flash.test.testcases.TestCallSetterGetter(); - new com.jpexs.flash.test.testcases.TestVarsMethods(); - new com.jpexs.flash.test.testcases.TestMaintainOrder(); + new com.jpexs.flash.test.testcases.TestSetterGetter(); + new com.jpexs.flash.test.testcases.TestCallSetterGetter(); + new com.jpexs.flash.test.testcases.TestVarsMethods(); + new com.jpexs.flash.test.testcases.TestMaintainOrder(); } } \ No newline at end of file