diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df8d40b3..045558141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file. - Rename invalid identifiers renames identifiers with a dollar sign - [#1676] Messages on movie tags when Flash Player ActiveX not available - [#1677] DefineFont2/3 - missing codeTableOffset if numGlyphs is zero and font has layout +- AS decompilation - §§push before loop ## [14.3.1] - 2021-03-25 ### Fixed 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 606c3b5f5..aeef14a0f 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 @@ -1611,7 +1611,7 @@ public class AVM2Graph extends Graph { } @Override - protected GraphTargetItem checkLoop(List output, LoopItem loopItem, BaseLocalData localData, List loops, List throwStates) { + protected GraphTargetItem checkLoop(List output, LoopItem loopItem, BaseLocalData localData, List loops, List throwStates, TranslateStack stack) { AVM2LocalData aLocalData = (AVM2LocalData) localData; if (loopItem instanceof WhileItem) { WhileItem w = (WhileItem) loopItem; @@ -1620,6 +1620,22 @@ public class AVM2Graph extends Graph { HasNextAVM2Item hn = (HasNextAVM2Item) w.expression.get(w.expression.size() - 1); if (hn.obj != null) { if (hn.obj.getNotCoerced().getThroughRegister().getNotCoerced() instanceof FilteredCheckAVM2Item) { + + //All items are moved from stack to output before entering while, + // this code block moves them back to stack + int pushnum = 0; + for (int i = output.size() - 2 /*last is loop*/; i >= 0; i--) { + if (output.get(i) instanceof PushItem) { + pushnum++; + } else { + break; + } + } + int rem = output.size() - 1 - pushnum; + for (int i = output.size() - 1 - pushnum; i <= output.size() - 2; i++) { + stack.push(((PushItem) output.remove(rem)).value); + } + //---------- end moving back to stack if (w.commands.size() >= 3) { int pos = 0; Set localRegsToKill = new HashSet<>(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java index 95a4c9de8..ec9c4b1fe 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/FunctionActionItem.java @@ -35,6 +35,7 @@ import com.jpexs.decompiler.graph.GraphSourceItemPos; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.SourceGenerator; +import com.jpexs.decompiler.graph.model.BranchStackResistant; import com.jpexs.decompiler.graph.model.LocalData; import com.jpexs.helpers.Helper; import java.util.ArrayList; @@ -48,7 +49,7 @@ import java.util.Set; * * @author JPEXS */ -public class FunctionActionItem extends ActionItem { +public class FunctionActionItem extends ActionItem implements BranchStackResistant { public List actions; 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 a7f6c86c5..f02f992df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1350,7 +1350,7 @@ public class Graph { return printGraph(foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, parent, part, stopPart, stopPartKind, loops, throwStates, null, staticOperation, path, 0); } - protected GraphTargetItem checkLoop(List output, LoopItem loopItem, BaseLocalData localData, List loops, List throwStates) { + protected GraphTargetItem checkLoop(List output, LoopItem loopItem, BaseLocalData localData, List loops, List throwStates, TranslateStack stack) { return loopItem; } @@ -1901,6 +1901,9 @@ public class Graph { } } + if (isLoop) { + makeAllCommands(ret, stack); + } if (debugPrintGraph) { System.err.println("loopsize:" + loops.size()); } @@ -2675,7 +2678,7 @@ public class Graph { } currentLoop.phase = 2; - GraphTargetItem replaced = checkLoop(ret, li, localData, loops, throwStates); + GraphTargetItem replaced = checkLoop(ret, li, localData, loops, throwStates, sPreLoop); if (replaced != li) { int index = ret.indexOf(li); ret.remove(index); @@ -2951,21 +2954,18 @@ public class Graph { clen--; } } - while (stack.size() > 0) { - GraphTargetItem p = stack.pop(); + for (int i = stack.size() - 1; i >= 0; i--) { + GraphTargetItem p = stack.get(i); if (p instanceof BranchStackResistant) { continue; } + stack.remove(i); if (!(p instanceof PopItem)) { - if (p instanceof FunctionActionItem) { + if (isExit) { + //ASC2 leaves some function calls unpopped on stack before returning from a method commands.add(clen, p); } else { - if (isExit) { - //ASC2 leaves some function calls unpopped on stack before returning from a method - commands.add(clen, p); - } else { - commands.add(clen, new PushItem(p)); - } + commands.add(clen, new PushItem(p)); } } } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java index b464e472b..e95a32d4a 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3AssembledDecompileTest.java @@ -131,6 +131,22 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT false); } + @Test + public void testPushWhile() { + decompileMethod("assembled", "testPushWhile", "var _loc3_:int = 5;\r\n" + + "§§push(obfuscated[\"xxx\"] = new [\"ByteArray\"]());\r\n" + + "§§push(50);\r\n" + + "while(§§dup(§§pop()))\r\n" + + "{\r\n" + + "§§dup(§§pop())[§§dup(§§dup(§§pop())).length] = 41 ^ 111;\r\n" + + "§§dup(§§pop())[§§dup(§§dup(§§pop())).length] = 9 ^ 84;\r\n" + + "§§push(§§pop() - 1);\r\n" + + "}\r\n" + + "§§pop();\r\n" + + "§§pop();\r\n", + false); + } + @Test public void testSetSlotDup() { decompileMethod("assembled", "testSetSlotDup", "var _loc5_:int = 5;\r\n" diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc index 782886fa7..7a52bbf8e 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc and b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.abc differ diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm index bd01e338c..489896ae4 100644 --- a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/as3_assembled-0.main.asasm @@ -28,5 +28,6 @@ program #include "tests/TestDecrementPrecedence.script.asasm" #include "tests/TestSwitchGoto.script.asasm" #include "tests/TestTryWhile.script.asasm" + #include "tests/TestPushWhile.script.asasm" ; place to add next end ; program diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestPushWhile.class.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestPushWhile.class.asasm new file mode 100644 index 000000000..8977cc6ab --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestPushWhile.class.asasm @@ -0,0 +1,99 @@ +class + refid "tests:TestPushWhile" + instance QName(PackageNamespace("tests"), "TestPushWhile") + extends QName(PackageNamespace(""), "Object") + flag SEALED + flag PROTECTEDNS + protectedns ProtectedNamespace("tests:TestPushWhile") + iinit + refid "tests:TestPushWhile/instance/init" + body + maxstack 1 + localcount 1 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + + getlocal0 + constructsuper 0 + + returnvoid + end ; code + end ; body + end ; method + trait method QName(PackageNamespace(""), "run") + method + refid "tests:TestPushWhile/instance/run" + returns QName(PackageNamespace(""), "void") + body + maxstack 2 + localcount 4 + initscopedepth 4 + maxscopedepth 5 + code + getlocal0 + pushscope + pushbyte 5 + setlocal 3 + + pushstring "ByteArray" + dup + findpropstrict MultinameL([PackageNamespace("flash.utils","1")]) + swap + constructprop MultinameL([PackageNamespace("flash.utils","1")]), 0 + dup + getlex QName(PackageNamespace("","2"),"obfuscated") + swap + pushstring "xxx" + swap + setproperty MultinameL([PackageNamespace("","2")]) + pushint 50 + ofs0016: + label + dup + iffalse ofs0039 + decrement_i + swap + dup + dup + getproperty QName(PackageNamespace("","2"),"length") + pushbyte 41 + pushbyte 111 + bitxor + setproperty MultinameL([PackageNamespace("","2")]) + dup + dup + getproperty QName(PackageNamespace("","2"),"length") + pushbyte 9 + pushbyte 84 + bitxor + setproperty MultinameL([PackageNamespace("","2")]) + swap + jump ofs0016 + ofs0039: + pop + pop + returnvoid + end ; code + end ; body + end ; method + end ; trait + end ; instance + cinit + refid "tests:TestPushWhile/class/init" + body + maxstack 1 + localcount 1 + initscopedepth 3 + maxscopedepth 4 + code + getlocal0 + pushscope + + returnvoid + end ; code + end ; body + end ; method +end ; class diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestPushWhile.script.asasm b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestPushWhile.script.asasm new file mode 100644 index 000000000..57fea0452 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_assembled/abc/as3_assembled-0/tests/TestPushWhile.script.asasm @@ -0,0 +1,29 @@ +script + sinit + refid "tests:TestPushWhile/init" + body + maxstack 2 + localcount 1 + initscopedepth 1 + maxscopedepth 3 + code + getlocal0 + pushscope + + findpropstrict Multiname("TestPushWhile", [PackageNamespace("tests")]) + getlex QName(PackageNamespace(""), "Object") + pushscope + + getlex Multiname("Object", [PrivateNamespace(null, "tests:TestPushWhile"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")]) + newclass "tests:TestPushWhile" + popscope + initproperty QName(PackageNamespace("tests"), "TestPushWhile") + + returnvoid + end ; code + end ; body + end ; method + trait class QName(PackageNamespace("tests"), "TestPushWhile") + #include "TestPushWhile.class.asasm" + end ; trait +end ; script diff --git a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf index 51679b492..6fdb3d7a9 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf and b/libsrc/ffdec_lib/testdata/as3_assembled/bin/as3_assembled.swf differ