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 984cd6193..b3d3b5ab8 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 @@ -116,6 +116,7 @@ import com.jpexs.decompiler.graph.model.NotItem; import com.jpexs.decompiler.graph.model.OrItem; import com.jpexs.decompiler.graph.model.PushItem; import com.jpexs.decompiler.graph.model.SwitchItem; +import com.jpexs.decompiler.graph.model.TrueItem; import com.jpexs.decompiler.graph.model.WhileItem; import com.jpexs.helpers.Reference; import java.util.ArrayList; @@ -2424,17 +2425,8 @@ public class AVM2Graph extends Graph { return false; } - /** - * Final process. - * - * @param list List of GraphTargetItems - * @param level Level - * @param localData Local data - * @param path Path - * @throws InterruptedException On interrupt - */ @Override - protected void finalProcess(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { + protected void finalProcess(GraphTargetItem parent, List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { if (debugDoNotProcess) { return; } @@ -2655,6 +2647,7 @@ public class AVM2Graph extends Graph { AVM2FinalProcessLocalData adata = (AVM2FinalProcessLocalData) localData; //if(false) if (!adata.bottomSetLocals.isEmpty()) { + boolean modified = false; for (int i = 0; i < list.size(); i++) { if (list.get(i) instanceof LoopItem) { @@ -2664,7 +2657,7 @@ public class AVM2Graph extends Graph { continue; } - Reference foundSetLoc = new Reference<>(null); + List foundSetLoc = new ArrayList<>(); List ignoredItems = new ArrayList<>(); //We need to ignore everything on the right side of && and ||, @@ -2706,26 +2699,45 @@ public class AVM2Graph extends Graph { GraphTargetItem parent = parentStack.get(s); boolean move = true; if (parent instanceof SetTypeAVM2Item) { - SetTypeAVM2Item setType = (SetTypeAVM2Item) parent; - if (setType.getValue().getNotCoerced() == item || setType.getObject() == item) { //chained assignment - move = false; + SetTypeAVM2Item setType = (SetTypeAVM2Item) parent; + + if (setType.getValue().getNotCoerced() == item) { //chained assignment + //if (!((parent instanceof SetLocalAVM2Item) + // && ((SetLocalAVM2Item)parent).regIndex == ((SetLocalAVM2Item) item).regIndex)) { //no chain for the same localreg + move = false; } } if (move) { - foundSetLoc.setVal((SetLocalAVM2Item) item); + foundSetLoc.add(0, (SetLocalAVM2Item) item); } } } } } }); - if (foundSetLoc.getVal() != null) { - SetLocalAVM2Item setLoc = foundSetLoc.getVal(); + for (SetLocalAVM2Item setLoc : foundSetLoc) { list.add(i, setLoc.clone()); - setLoc.hideValue = true; + setLoc.hideValue = true; i++; + modified = true; + adata.bottomSetLocals.remove(setLoc); } } + if (modified && (parent instanceof WhileItem)) { + WhileItem wi = (WhileItem) parent; + if (wi.expression == list && !list.isEmpty()) { + GraphTargetItem lastExpr = list.remove(list.size() - 1); + List onTrue = new ArrayList<>(); + BreakItem bi = new BreakItem(null, null, wi.loop.id); + onTrue.add(bi); + IfItem ifi = new IfItem(null, null, lastExpr.invert(null), onTrue, new ArrayList<>()); + list.add(ifi); + wi.commands.addAll(0, list); + list.clear(); + list.add(new TrueItem(null, null)); + } + } + } /* @@ -2859,7 +2871,7 @@ public class AVM2Graph extends Graph { } */ //Handle for loops at the end: - super.finalProcess(list, level, localData, path); + super.finalProcess(parent, list, level, localData, path); } @Override 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 84f9dcb53..47808d2de 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 @@ -274,17 +274,8 @@ public class ActionGraph extends Graph { return !isSwitch; } - /** - * Final process. - * - * @param list List of GraphTargetItems - * @param level Level - * @param localData Local data - * @param path Path - * @throws InterruptedException On interrupt - */ @Override - protected void finalProcess(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { + protected void finalProcess(GraphTargetItem parent, List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { if (level == 0) { List removed = new ArrayList<>(); @@ -553,7 +544,7 @@ public class ActionGraph extends Graph { } } //Handle for loops at the end: - super.finalProcess(list, level, localData, path); + super.finalProcess(parent, list, level, localData, path); } 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 8f971b61f..c009b8c02 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1001,7 +1001,7 @@ public class Graph { processIfs(ret); finalProcessStack(stack, ret, path); makeAllCommands(ret, stack); - finalProcessAll(ret, 0, getFinalData(localData, loops, throwStates), path); + finalProcessAll(null, ret, 0, getFinalData(localData, loops, throwStates), path); return ret; } @@ -1332,22 +1332,23 @@ public class Graph { /** * Final process all. * + * @param parent Parent item * @param list List of GraphTargetItems * @param level Level * @param localData Local data * @param path Path * @throws InterruptedException On interrupt */ - private void finalProcessAll(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { + private void finalProcessAll(GraphTargetItem parent, List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { if (debugDoNotProcess) { return; } - finalProcess(list, level, localData, path); + finalProcess(parent, list, level, localData, path); for (GraphTargetItem item : list) { if (item instanceof Block) { List> subs = ((Block) item).getSubs(); for (List sub : subs) { - finalProcessAll(sub, level + 1, localData, path); + finalProcessAll(item, sub, level + 1, localData, path); } } } @@ -1428,13 +1429,14 @@ public class Graph { /** * Final process. Override this method to provide custom behavior. * + * @param parent Paren item * @param list List of GraphTargetItems * @param level Level * @param localData Local data * @param path Path * @throws InterruptedException On interrupt */ - protected void finalProcess(List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { + protected void finalProcess(GraphTargetItem parent, List list, int level, FinalProcessLocalData localData, String path) throws InterruptedException { //For detection based on debug line information boolean[] toDelete = new boolean[list.size()]; diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java index 3bf9f7273..51c0985d7 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java @@ -1708,6 +1708,20 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile false); } + @Test + public void testOptimizationWhile() { + decompileMethod("classic_air", "testOptimizationWhile", "var a:int = 1;\r\n" + + "var b:int = 2;\r\n" + + "var c:int = 3;\r\n" + + "var d:int = 4;\r\n" + + "while(d = Math.round(Math.random() * 10), d < 10)\r\n" + + "{\r\n" + + "trace(\"xxx\");\r\n" + + "d++;\r\n" + + "}\r\n", + false); + } + @Test public void testParamNames() { decompileMethod("classic_air", "testParamNames", "return firstp + secondp + thirdp;\r\n", diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java index 5e8b04586..245d696f7 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java @@ -1694,6 +1694,25 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes false); } + @Test + public void testOptimizationWhile() { + decompileMethod("classic", "testOptimizationWhile", "var a:int = 1;\r\n" + + "var b:int = 2;\r\n" + + "var c:int = 3;\r\n" + + "var d:int = 4;\r\n" + + "while(true)\r\n" + + "{\r\n" + + "d = Math.round(Math.random() * 10);\r\n" + + "if(d >= 10)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"xxx\");\r\n" + + "d++;\r\n" + + "}\r\n", + false); + } + @Test public void testParamNames() { decompileMethod("classic", "testParamNames", "return firstp + secondp + thirdp;\r\n", diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf index 09e59b4f3..4e176eb7f 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf index 6189a737c..072d8bae4 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as index f4f2f0b89..a06a37b1d 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as @@ -100,6 +100,7 @@ package TestOperations; TestOptimization; TestOptimizationAndOr; + TestOptimizationWhile; TestOptionalParameters; TestParamNames; TestParamsCount; diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestOptimizationWhile.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestOptimizationWhile.as new file mode 100644 index 000000000..c0020a994 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestOptimizationWhile.as @@ -0,0 +1,29 @@ +package tests +{ + + public class TestOptimizationWhile + { + public function run():* + { + // Add more than 3 variables. + // Optimization happens from register 4 on. + // (setlocal X takes more bytes than dup) + var a:int = 1; + var b:int = 2; + var c:int = 3; + + var d:int = 4; + + while(true) + { + d = Math.round(Math.random() * 10); + if(d >= 10) + { + break; + } + trace("xxx"); + d++; + } + } + } +}