diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 63d38f28e..da3d61b4f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -674,6 +674,75 @@ public class AVM2Code implements Cloneable { return null; } + public void calculateDebugFileLine(ABC abc) { + calculateDebugFileLine(null, 0, 0, abc, new HashSet()); + } + + private boolean calculateDebugFileLine(String debugFile, int debugLine, int pos, ABC abc, Set seen) { + while (pos < code.size()) { + AVM2Instruction ins = code.get(pos); + if (seen.contains(pos)) { + return true; + } + + seen.add(pos); + + if (ins.definition instanceof DebugFileIns) { + debugFile = abc.constants.getString(ins.operands[0]); + } + + if (ins.definition instanceof DebugLineIns) { + debugLine = ins.operands[0]; + } + + ins.setFileLine(debugFile, debugLine); + + if (ins.definition instanceof NewFunctionIns) { + MethodBody innerBody = abc.findBody(ins.operands[0]); + innerBody.getCode().calculateDebugFileLine(debugFile, debugLine, 0, abc, new HashSet()); + } + + if (ins.definition instanceof ReturnValueIns) { + return true; + } + if (ins.definition instanceof ReturnVoidIns) { + return true; + } + if (ins.definition instanceof JumpIns) { + try { + pos = adr2pos(pos2adr(pos) + ins.getBytes().length + ins.operands[0]); + continue; + } catch (ConvertException ex) { + return false; + } + } else if (ins.definition instanceof IfTypeIns) { + try { + int newpos = adr2pos(pos2adr(pos) + ins.getBytes().length + ins.operands[0]); + calculateDebugFileLine(debugFile, debugLine, newpos, abc, seen); + } catch (ConvertException ex) { + return false; + } + } + if (ins.definition instanceof LookupSwitchIns) { + for (int i = 0; i < ins.operands.length; i++) { + if (i == 1) { + continue; + } + try { + int newpos = adr2pos(pos2adr(pos) + ins.operands[i]); + if (!calculateDebugFileLine(debugFile, debugLine, newpos, abc, seen)) { + return false; + } + } catch (ConvertException ex) { + return false; + } + } + } + pos++; + } + return true; + } + public AVM2Code(ABCInputStream ais) throws IOException { Map codeMap = new TreeMap<>(); Map endOffsets = new HashMap<>(); 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 7e8f6ed17..616ef7bbb 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 @@ -726,6 +726,7 @@ public class AVM2Graph extends Graph { @Override protected void finalProcess(List list, int level, FinalProcessLocalData localData) { + super.finalProcess(list, level, localData); if (level == 0) { if (!list.isEmpty()) { if (list.get(list.size() - 1) instanceof ReturnVoidAVM2Item) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java index 3904c0a29..c15483e34 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java @@ -79,6 +79,7 @@ public class AVM2GraphSource extends GraphSource { this.scriptIndex = scriptIndex; this.localRegAssigmentIps = localRegAssigmentIp; this.refs = refs; + code.calculateDebugFileLine(abc); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java index e2d82b59d..7bfae4a7e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/AVM2Instruction.java @@ -56,6 +56,15 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem { public int changeJumpTo = -1; + private int line; + + private String file; + + public void setFileLine(String file, int line) { + this.file = file; + this.line = line; + } + public AVM2Instruction(long offset, InstructionDefinition definition, int[] operands) { this.definition = definition; this.operands = operands != null && operands.length > 0 ? operands : null; @@ -382,4 +391,15 @@ public class AVM2Instruction implements Cloneable, GraphSourceItem { } return ret; } + + @Override + public int getLine() { + return line; + } + + @Override + public String getFile() { + return file; + } + } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java index 7137b6b02..4a8c85979 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/Action.java @@ -1287,4 +1287,15 @@ public abstract class Action implements GraphSourceItem { public boolean isDeobfuscatePop() { return false; } + + @Override + public int getLine() { + return 0; + } + + @Override + public String getFile() { + return null; + } + } 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 90d538cbd..4b726c89c 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 @@ -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.flash.action; import com.jpexs.decompiler.flash.BaseLocalData; @@ -93,7 +94,7 @@ public class ActionGraph extends Graph { @Override protected void finalProcess(List list, int level, FinalProcessLocalData localData) { - + super.finalProcess(list, level, localData); List ret = Action.checkClass(list); if (ret != list) { list.clear(); 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 648aa2abb..863b01459 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -461,6 +461,64 @@ public class Graph { } protected void finalProcess(List list, int level, FinalProcessLocalData localData) { + + //For detection based on debug line information + Set removeFromList = new HashSet<>(); + for (int i = 0; i < list.size(); i++) { + + if (list.get(i) instanceof ForItem) { + ForItem fori = (ForItem) list.get(i); + int exprLine = fori.getLine(); + if (exprLine > 0) { + List forFirstCommands = new ArrayList<>(); + for (int j = i - 1; j >= 0; j--) { + if (list.get(j).getLine() == exprLine) { + forFirstCommands.add(0, list.get(j)); + removeFromList.add(j); + } else { + break; + } + } + fori.firstCommands.addAll(0, forFirstCommands); + } + } + + if (list.get(i) instanceof WhileItem) { + WhileItem whi = (WhileItem) list.get(i); + int whileExprLine = whi.getLine(); + if (whileExprLine > 0) { + List forFirstCommands = new ArrayList<>(); + List forFinalCommands = new ArrayList<>(); + + for (int j = i - 1; j >= 0; j--) { + if (list.get(j).getLine() == whileExprLine) { + forFirstCommands.add(0, list.get(j)); + removeFromList.add(j); + } else { + break; + } + } + for (int j = whi.commands.size() - 1; j >= 0; j--) { + if (whi.commands.get(j).getLine() == whileExprLine) { + forFinalCommands.add(0, whi.commands.remove(j)); + } else { + break; + } + } + if (!forFirstCommands.isEmpty() || !forFinalCommands.isEmpty()) { + GraphTargetItem lastExpr = whi.expression.remove(whi.expression.size() - 1); + forFirstCommands.addAll(whi.expression); + list.set(i, new ForItem(whi.src, whi.loop, forFirstCommands, lastExpr, forFinalCommands, whi.commands)); + } + } + } + } + + for (int i = list.size() - 1; i >= 0; i--) { + if (removeFromList.contains(i)) { + list.remove(i); + } + } } private void processIfs(List list) { @@ -1752,9 +1810,9 @@ public class Graph { checkContinueAtTheEnd(finalComm, currentLoop); } if (!finalComm.isEmpty()) { - ret.add(index, li = new ForItem(null, currentLoop, new ArrayList(), exprList.get(exprList.size() - 1), finalComm, commands)); + ret.add(index, li = new ForItem(expr.src, currentLoop, new ArrayList(), exprList.get(exprList.size() - 1), finalComm, commands)); } else { - ret.add(index, li = new WhileItem(null, currentLoop, exprList, commands)); + ret.add(index, li = new WhileItem(expr.src, currentLoop, exprList, commands)); } loopTypeFound = true; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java index 6817d16dd..6d7208e92 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphSourceItem.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; @@ -44,4 +45,8 @@ public interface GraphSourceItem extends Serializable { public void setIgnored(boolean ignored, int pos); public boolean isDeobfuscatePop(); + + public int getLine(); + + public String getFile(); } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java index 5adcfbd01..b5c5aa4b5 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java @@ -83,6 +83,20 @@ public abstract class GraphTargetItem implements Serializable { protected HighlightData srcData = new HighlightData(); + public int getLine() { + if (src != null) { + return src.getLine(); + } + return 0; + } + + public String getFile() { + if (src != null) { + return src.getFile(); + } + return null; + } + public GraphPart getFirstPart() { if (value == null) { return firstPart; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/ForItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/ForItem.java index 44ab7b1f7..0f0c86545 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/ForItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/ForItem.java @@ -90,9 +90,9 @@ public class ForItem extends LoopItem implements Block { firstCommands.get(i).toString(writer, localData); p++; } - writer.append(";"); + writer.append("; "); expression.toString(writer, localData); - writer.append(";"); + writer.append("; "); p = 0; for (int i = 0; i < finalCommands.size(); i++) { if (finalCommands.get(i).isEmpty()) { 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 9a5053c37..ebe1c7c92 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2Test.java @@ -138,7 +138,7 @@ public class ActionScript2Test extends ActionStript2TestBase { public void frame28_forWithContinueTest() { compareSrc(28, "trace(\"forWithContinueTest\");\r\n" + "var i = 0;\r\n" - + "for(;i < 10;i++)\r\n" + + "for(; i < 10; i++)\r\n" + "{\r\n" + "trace(\"hello:\" + i);\r\n" + "if(i == 5)\r\n" @@ -213,7 +213,7 @@ public class ActionScript2Test extends ActionStript2TestBase { public void frame32_switchForTest() { compareSrc(32, "trace(\"switchForTest\");\r\n" + "var i = 0;\r\n" - + "for(;i < 10;i++)\r\n" + + "for(; i < 10; i++)\r\n" + "{\r\n" + "switch(i)\r\n" + "{\r\n" 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 62ae36521..c16762a13 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3Test.java @@ -247,12 +247,10 @@ public class ActionScript3Test extends ActionScriptTestBase { + "default:\r\n" + "trace(\"default clause\");\r\n" + "}\r\n" - + "c = 0;\r\n" + "loop1:\r\n" - + "for(;c < 8;c = c + 1)\r\n" + + "for(c = 0; c < 8; c = c + 1)\r\n" + "{\r\n" - + "d = 0;\r\n" - + "while(d < 25)\r\n" + + "for(d = 0; d < 25; d++)\r\n" + "{\r\n" + "e = 0;\r\n" + "if(e < 50)\r\n" @@ -270,7 +268,6 @@ public class ActionScript3Test extends ActionScriptTestBase { + "break loop1;\r\n" + "}\r\n" + "}\r\n" - + "d++;\r\n" + "}\r\n" + "trace(\"hello\");\r\n" + "}\r\n", false); @@ -315,17 +312,17 @@ public class ActionScript3Test extends ActionScriptTestBase { @Test public void testForBreak() { /*decompileMethod("testForBreak", "var a:* = 0;\r\n" - + "while(a < 10)\r\n" - + "{\r\n" - + "if(a == 5)\r\n" - + "{\r\n" - + "break;\r\n" - + "}\r\n" - + "trace(\"hello:\" + a);\r\n" - + "a++;\r\n" - + "}\r\n", false);*/ + + "while(a < 10)\r\n" + + "{\r\n" + + "if(a == 5)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"hello:\" + a);\r\n" + + "a++;\r\n" + + "}\r\n", false);*/ // Issue 842, recover for loops - decompileMethod("testForBreak", "for (var a:* = 0; a < 10; a++)\r\n" + decompileMethod("testForBreak", "for(var a:* = 0; a < 10; a++)\r\n" + "{\r\n" + "if(a == 5)\r\n" + "{\r\n" @@ -359,14 +356,7 @@ public class ActionScript3Test extends ActionScriptTestBase { @Test public void testFor() { - /*decompileMethod("testFor", "var a:* = 0;\r\n" - + "while(a < 10)\r\n" - + "{\r\n" - + "trace(\"a=\" + a);\r\n" - + "a++;\r\n" - + "}\r\n", false);*/ - // Issue 842, recover for loops - decompileMethod("testFor", "for (var a:* = 0; a < 10; a++)\r\n" + decompileMethod("testFor", "for(var a:* = 0; a < 10; a++)\r\n" + "{\r\n" + "trace(\"a=\" + a);\r\n" + "}\r\n", false); @@ -374,8 +364,7 @@ public class ActionScript3Test extends ActionScriptTestBase { @Test public void testForContinue() { - decompileMethod("testForContinue", "var a:* = 0;\r\n" - + "for(;a < 10;a = a + 1)\r\n" + decompileMethod("testForContinue", "for(var a:* = 0; a < 10; a = a + 1)\r\n" + "{\r\n" + "if(a == 9)\r\n" + "{\r\n" @@ -853,16 +842,13 @@ public class ActionScript3Test extends ActionScriptTestBase { @Test public void testWhileTry2() { decompileMethod("testWhileTry2", "var j:* = undefined;\r\n" - + "var i:* = 0;\r\n" - + "for(;i < 100;i++)\r\n" + + "for(var i:* = 0; i < 100; i++)\r\n" + "{\r\n" + "try\r\n" + "{\r\n" - + "j = 0;\r\n" - + "while(j < 20)\r\n" + + "for(j = 0; j < 20; j++)\r\n" + "{\r\n" + "trace(\"a\");\r\n" - + "j++;\r\n" + "}\r\n" + "}\r\n" + "catch(e:EOFError)\r\n"