diff --git a/CHANGELOG.md b/CHANGELOG.md index 458df1eaa..3e58948af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ All notable changes to this project will be documented in this file. - #1159, #1608 Regexp syntax hilight when not a regexp (only division) again - Graphviz Graph not showing AS3 exception end - #1609 First frame missing in frame to PDF export +- AS3 with statement decompilation ### Changed - #1565, #1407, #1350 On BinaryData SWF save, parent SWF is saved 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 3f78f2156..9206b3ccf 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 @@ -71,6 +71,7 @@ import com.jpexs.decompiler.flash.abc.avm2.model.SetTypeAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.ThrowAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.WithAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.WithEndAVM2Item; +import com.jpexs.decompiler.flash.abc.avm2.model.WithObjectAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ExceptionAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.FilterAVM2Item; import com.jpexs.decompiler.flash.abc.avm2.model.clauses.ForEachInAVM2Item; @@ -243,7 +244,6 @@ public class AVM2Graph extends Graph { List targetOutput = translatePart(localData2, finallyTryTargetPart, finallyTryTargetStack, 0 /*??*/, "try_target"); - int switchedReg = -1; int finallyKind = FINALLY_KIND_UNKNOWN; Integer finallyThrowPushByte = null; @@ -1018,7 +1018,7 @@ public class AVM2Graph extends Graph { localData2.scopeStack = new ScopeStack(); //We are assuming Finally target has only 1 part - finallyTargetItems = translatePart(localData, finallyTryTargetPart, st2, staticOperation, path);//printGraph(foundGotos, partCodes, partCodePos, visited, localData2, st2, allParts, null, finallyTryTargetPart, finallyTargetStopPart, loops, throwStates, 0, path); + finallyTargetItems = translatePart(localData2, finallyTryTargetPart, st2, staticOperation, path);//printGraph(foundGotos, partCodes, partCodePos, visited, localData2, st2, allParts, null, finallyTryTargetPart, finallyTargetStopPart, loops, throwStates, 0, path); //boolean targetHasThrow = false; if (!finallyTargetItems.isEmpty() && (finallyTargetItems.get(finallyTargetItems.size() - 1) instanceof ThrowAVM2Item)) { @@ -1132,6 +1132,30 @@ public class AVM2Graph extends Graph { currentCatchCommands.remove(0); } }*/ + loopwith: + while (!currentCatchCommands.isEmpty() && (currentCatchCommands.get(0) instanceof WithAVM2Item)) { + WithAVM2Item w = (WithAVM2Item) currentCatchCommands.get(0); + if (w.scope instanceof LocalRegAVM2Item) { + int regId = ((LocalRegAVM2Item) w.scope).regIndex; + for (GraphTargetItem item : localData.scopeStack) { + if (item instanceof WithObjectAVM2Item) { + WithObjectAVM2Item wo = (WithObjectAVM2Item) item; + + if (wo.scope instanceof SetLocalAVM2Item) { + SetLocalAVM2Item setLocal = (SetLocalAVM2Item) wo.scope; + if (setLocal.regIndex == regId) { + currentCatchCommands.remove(0); + int setLocalIp = localData.code.adr2pos(setLocal.getSrc().getAddress()); + int getLocalIp = localData.code.adr2pos(w.scope.getSrc().getAddress()); + localData.setLocalPosToGetLocalPos.get(setLocalIp).remove(getLocalIp); + continue loopwith; + } + } + } + } + } + break; //its a brand new with inside catch clause + } if (!currentCatchCommands.isEmpty() && (currentCatchCommands.get(currentCatchCommands.size() - 1) instanceof SetLocalAVM2Item)) { SetLocalAVM2Item setLocal = (SetLocalAVM2Item) currentCatchCommands.get(currentCatchCommands.size() - 1); if (setLocal.regIndex == switchedReg) { @@ -1160,21 +1184,18 @@ public class AVM2Graph extends Graph { && (((SetLocalAVM2Item) currentRet.get(currentRet.size() - 1)).regIndex == switchedReg)) { currentRet.remove(currentRet.size() - 1); } - - if (!finallyAsUnnamedException && !inlinedFinally && catchedExceptions.isEmpty() && finallyCommands.isEmpty()) { currentRet.addAll(tryCommands); return true; } - if (finallyAsUnnamedException) - { + if (finallyAsUnnamedException) { catchedExceptions.add(finallyException); catchCommands.add(finallyCommands); finallyCommands = new ArrayList<>(); } - + TryAVM2Item tryItem = new TryAVM2Item(tryCommands, catchedExceptions, catchCommands, finallyCommands, ""); if (inlinedFinally) { List> parentCatchCommands = new ArrayList<>(); @@ -1871,6 +1892,26 @@ public class AVM2Graph extends Graph { } for (int i = 0; i < list.size(); i++) { + + if (list.get(i) instanceof WithAVM2Item) { + WithAVM2Item wa = (WithAVM2Item) list.get(i); + if (wa.scope instanceof SetLocalAVM2Item) { + SetLocalAVM2Item setLocal = (SetLocalAVM2Item) wa.scope; + int setLocalIp = avm2code.adr2pos(setLocal.getSrc().getAddress()); + if (localData.getRegisterUsage(setLocalIp).isEmpty()) { + for (int j = i + 1; j < list.size(); j++) { + if (list.get(j) instanceof WithEndAVM2Item) { + WithEndAVM2Item we = (WithEndAVM2Item) list.get(j); + if (we.scope == wa.scope) { + wa.scope = we.scope = setLocal.value; + } + } + } + + } + } + } + if (list.get(i) instanceof SetLocalAVM2Item) { SetLocalAVM2Item ri = (SetLocalAVM2Item) list.get(i); int setLocalIp = avm2code.adr2pos(ri.getSrc().getAddress()); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/WithAVM2Item.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/WithAVM2Item.java index 5016f0dfb..22be7686e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/WithAVM2Item.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/model/WithAVM2Item.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.abc.avm2.model; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; @@ -77,6 +78,16 @@ public class WithAVM2Item extends AVM2Item { return false; } + @Override + public boolean needsNewLine() { + return false; + } + + @Override + public boolean handlesNewLine() { + return true; + } + @Override public GraphTargetItem returnType() { return TypeItem.UNBOUNDED; 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 f29ea1f75..475743f17 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -2585,7 +2585,10 @@ public class Graph { public static GraphTextWriter graphToString(List tree, GraphTextWriter writer, LocalData localData) throws InterruptedException { for (GraphTargetItem ti : tree) { if (!ti.isEmpty()) { - ti.toStringSemicoloned(writer, localData).newLine(); + ti.toStringSemicoloned(writer, localData); + if (!ti.handlesNewLine()) { + writer.newLine(); + } } } return writer; 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 2aba9e2a9..26e95c5df 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphTargetItem.java @@ -433,6 +433,10 @@ public abstract class GraphTargetItem implements Serializable, Cloneable { return false; } + public boolean handlesNewLine() { + return false; + } + public GraphTextWriter toStringNL(GraphTextWriter writer, LocalData localData) throws InterruptedException { writer.startOffset(src, getLineStartItem(), getPos(), srcData); appendTry(writer, localData); 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 d7541037d..a91795b3a 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 @@ -18,10 +18,8 @@ public class ActionScript3AssembledDecompileTest extends ActionScript3DecompileT @Test public void testDeclareReg() { - decompileMethod("assembled", "testDeclareReg", "var _loc3_:*;\r\n" - + "with(_loc3_ = other)\r\n" + decompileMethod("assembled", "testDeclareReg", "with(other)\r\n" + "{\r\n" - + "\r\n" + "trace(\"hey\");\r\n" + "}\r\n", false); diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileDecompileTest.java index f8b54fe37..af068835b 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileDecompileTest.java @@ -513,6 +513,28 @@ public class ActionScript3CrossCompileDecompileTest extends ActionScript3Decompi false); } + @Test(dataProvider = "swfNamesProvider") + public void testTryCatchWith(String swfUsed) { + decompileMethod(swfUsed, "testTryCatchWith", "var a:MyTest = new MyTest();\r\n" + + "trace(\"before with\");\r\n" + + "with(a)\r\n" + + "{\r\n" + + "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "attrib = attrib + 1;\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "trace(\"after try\");\r\n" + + "}\r\n" + + "trace(\"after\");\r\n", + false); + } + @Test(dataProvider = "swfNamesProvider") public void testTryFinally(String swfUsed) { decompileMethod(swfUsed, "testTryFinally", "trace(\"before try\");\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java index 9902b6426..81fd56421 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3CrossCompileSwfToolsDecompileTest.java @@ -504,6 +504,32 @@ public class ActionScript3CrossCompileSwfToolsDecompileTest extends ActionScript false); } + @Test + public void testTryCatchWith() { + decompileMethod("swftools", "testTryCatchWith", "var _loc1_:* = new MyTest();\r\n" + + "trace(\"before with\");\r\n" + + "var _loc2_:*;\r\n" + + "with(_loc2_ = _loc1_)\r\n" + + "{\r\n" + + "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "with(_loc2_)\r\n" + + "{\r\n" + + "\r\n" + + "attrib = attrib + 1;\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "trace(\"after try\");\r\n" + + "}\r\n" + + "trace(\"after\");\r\n", + false); + } + @Test public void testTryFinally() { decompileMethod("swftools", "testTryFinally", "trace(\"before try\");\r\n" diff --git a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.air.swf b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.air.swf index b064ff816..24bf677b9 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.air.swf and b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex.swf b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex.swf index 1cfda5c23..14e59fb6c 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex.swf and b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex_apache.swf b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex_apache.swf index 07ed29e98..d5b97bbb3 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex_apache.swf and b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.flex_apache.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.swftools.swf b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.swftools.swf index ea6da3b95..48a7fc4ea 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.swftools.swf and b/libsrc/ffdec_lib/testdata/as3_cross_compile/bin/as3_cross_compile.swftools.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_cross_compile/src/Main.as b/libsrc/ffdec_lib/testdata/as3_cross_compile/src/Main.as index 83afa9c8f..4d2067696 100644 --- a/libsrc/ffdec_lib/testdata/as3_cross_compile/src/Main.as +++ b/libsrc/ffdec_lib/testdata/as3_cross_compile/src/Main.as @@ -28,6 +28,8 @@ package TestTryCatchLoopBreak6; TestTryCatchReturn; TestTryCatchExceptionUsage + TestTryCatchTry; + TestTryCatchWith; TestTryFinally; TestTryFinallyDirectReturnInFinally; TestTryFinallyLoop; @@ -38,8 +40,7 @@ package TestTryFinallyReturnInFinally; TestTryFinallyReturnNested; TestTryFinallyReturnNested2; - TestTryFinallyReturnVoid; - TestTryCatchTry; + TestTryFinallyReturnVoid; public function Main() { diff --git a/libsrc/ffdec_lib/testdata/as3_cross_compile/src/tests/TestTryCatchWith.as b/libsrc/ffdec_lib/testdata/as3_cross_compile/src/tests/TestTryCatchWith.as new file mode 100644 index 000000000..04afa06b4 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_cross_compile/src/tests/TestTryCatchWith.as @@ -0,0 +1,39 @@ +package tests +{ + /** + * ... + * @author JPEXS + */ + public class TestTryCatchWith + { + + public function run() : void + { + var a:MyTest = new MyTest(); + + trace("before with"); + with (a) + { + trace("before try"); + try + { + trace("in try"); + } + catch (e:Error) + { + attrib = attrib + 1; + trace("in catch"); + } + trace("after try"); + } + trace("after"); + } + + } + +} + +class MyTest +{ + public var attrib:int = 5; +} \ No newline at end of file