From 4b48b8408a810f96d1d7c58b7bc93712fb07e759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Fri, 22 Aug 2025 19:29:04 +0200 Subject: [PATCH] Fixed: #2519 AS1/2 avoid multi-level loops in cases where possible --- CHANGELOG.md | 2 + .../src/com/jpexs/decompiler/graph/Graph.java | 8 +- .../decompiler/graph/model/BreakItem.java | 9 +++ .../flash/ActionScript2AssemblerTest.java | 76 +++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 484c77b3d..014929af3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ All notable changes to this project will be documented in this file. - [#2516] AS1/2 Renaming of identifiers not setting function length correctly - AS1/2 Renaming of identifiers must not rename integer array offsets - [#2517] Loop break detection problems in some cases +- [#2519] AS1/2 avoid multi-level loops in cases where possible ### Changed - Icon of "Deobfuscation options" menu from pile of pills to medkit @@ -3987,6 +3988,7 @@ Major version of SWF to XML export changed to 2. [#2515]: https://www.free-decompiler.com/flash/issues/2515 [#2516]: https://www.free-decompiler.com/flash/issues/2516 [#2517]: https://www.free-decompiler.com/flash/issues/2517 +[#2519]: https://www.free-decompiler.com/flash/issues/2519 [#2476]: https://www.free-decompiler.com/flash/issues/2476 [#2404]: https://www.free-decompiler.com/flash/issues/2404 [#1418]: https://www.free-decompiler.com/flash/issues/1418 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 8bf286b4c..f7a5111b9 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -3287,7 +3287,13 @@ public class Graph { System.err.println("Adding break"); } makeAllCommands(ret, stack); - ret.add(new BreakItem(dialect, null, localData.lineStartInstruction, el.id)); + + BreakItem br = new BreakItem(dialect, null, localData.lineStartInstruction, el.id); + if (part.start >= code.size() - 1) { + br.isScriptEnd = true; + } + ret.add(br); + return ret; } if (el.loopPreContinue == part) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BreakItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BreakItem.java index 97cf112f4..c39056f16 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BreakItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/model/BreakItem.java @@ -44,6 +44,11 @@ public class BreakItem extends GraphTargetItem { */ private boolean labelRequired; + /** + * Is placed at end of the script (or function) + */ + public boolean isScriptEnd = false; + /** * Constructor. * @@ -59,6 +64,10 @@ public class BreakItem extends GraphTargetItem { @Override public GraphTextWriter appendTo(GraphTextWriter writer, LocalData localData) { + if (isScriptEnd) { + writer.append("return"); + return writer; + } writer.append("break"); if (writer instanceof NulWriter) { NulWriter nulWriter = (NulWriter) writer; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java index 91d222738..c48912713 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript2AssemblerTest.java @@ -496,4 +496,80 @@ public class ActionScript2AssemblerTest extends ActionScript2TestBase { + "trace(\"C\");\n" + "}"); } + + @Test + public void testBreakReturnAsJumpAfterFunction2() { + String res = decompilePcode("ConstantPool \"a\", \"v\", \"b\", \"ret\"\n" + + "DefineFunction \"f\", 0 {\n" + + "Push \"a\"\n" + + "Push 3\n" + + "Push 2\n" + + "Push 1\n" + + "Push 3\n" + + "InitArray\n" + + "DefineLocal\n" + + "Push \"a\"\n" + + "GetVariable\n" + + "Enumerate2\n" + + "loc0046:StoreRegister 0\n" + + "Push null\n" + + "Equals2\n" + + "If loc00cb\n" + + "Push \"v\"\n" + + "Push register0\n" + + "SetVariable\n" + + "Push \"v\"\n" + + "GetVariable\n" + + "Trace\n" + + "Push \"b\"\n" + + "Push 0\n" + + "DefineLocal\n" + + "loc0074:Push \"b\"\n" + + "GetVariable\n" + + "Push 10\n" + + "Less2\n" + + "Not\n" + + "If loc00c6\n" + + "Push \"b\"\n" + + "GetVariable\n" + + "Push 4\n" + + "Equals2\n" + + "Not\n" + + "If loc00b4\n" + + "Push \"ret\"\n" + + "Trace\n" + + "loc00a4:Push null\n" + + "Equals2\n" + + "Not\n" + + "If loc00a4\n" + + "Jump loc00cb\n" + + "loc00b4:Push \"b\"\n" + + "Push \"b\"\n" + + "GetVariable\n" + + "Increment\n" + + "SetVariable\n" + + "Jump loc0074\n" + + "loc00c6:Jump loc0046\n" + + "}\n" + + "loc00cb:"); + res = cleanPCode(res); + assertEquals(res, "function f()\n" + + "{\n" + + "var a = [1,2,3];\n" + + "for(v in a)\n" + + "{\n" + + "trace(v);\n" + + "var b = 0;\n" + + "while(b < 10)\n" + + "{\n" + + "if(b == 4)\n" + + "{\n" + + "trace(\"ret\");\n" + + "return;\n" //critical - no level2 break, but return + + "}\n" + + "b++;\n" + + "}\n" + + "}\n" + + "}"); + } }