From f6744eafb94c6a54b4b5cd42674838c0076f6495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Tue, 2 Feb 2021 09:42:38 +0100 Subject: [PATCH] Fixed: Try..catch.finally vs loops --- .../decompiler/flash/abc/AVM2LocalData.java | 17 +- .../flash/abc/avm2/graph/AVM2Graph.java | 220 +++++---- .../jpexs/decompiler/flash/action/Action.java | 4 +- .../decompiler/flash/action/ActionGraph.java | 6 +- .../flash/action/swf7/ActionTry.java | 78 +-- .../src/com/jpexs/decompiler/graph/Graph.java | 48 +- .../com/jpexs/decompiler/graph/GraphPart.java | 25 +- .../ActionScript3ClassicDecompileTest.java | 17 +- ...ctionScript3CrossCompileDecompileTest.java | 62 +++ ...ipt3CrossCompileSwfToolsDecompileTest.java | 463 ++++++++++++++++++ .../flash/generators/AS3Generator.java | 2 + .../testdata/cross_compile/bin/Main.air.swf | Bin 2872 -> 3154 bytes .../testdata/cross_compile/bin/Main.flex.swf | Bin 3846 -> 4317 bytes .../cross_compile/bin/Main.flex_apache.swf | Bin 3856 -> 4288 bytes .../cross_compile/bin/Main.swftools.swf | Bin 1626 -> 1832 bytes .../cross_compile/obj/cross_compileConfig.old | 2 +- .../cross_compile/obj/cross_compileConfig.xml | 2 +- .../testdata/cross_compile/src/Main.as | 2 + .../src/tests/TestTryCatchInWhile.as | 41 ++ .../src/tests/TestTryCatchInWhile2.as | 48 ++ 20 files changed, 866 insertions(+), 171 deletions(-) create mode 100644 libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileSwfToolsDecompileTest.java create mode 100644 libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile.as create mode 100644 libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile2.as diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java index fb6dc3b65..ef339c62d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/AVM2LocalData.java @@ -74,10 +74,20 @@ public class AVM2LocalData extends BaseLocalData { public Map finallyIndexToDefaultGraphPart = new HashMap<>(); //exception index => switchPart - public Map ignoredSwitches; + public Map ignoredSwitches = new HashMap<>(); + + /** + * exception index -> switch defaultPart + */ + public Map defaultParts = new HashMap<>(); public Map switchedRegs = new HashMap<>(); + /** + * exception index -> switch throw part + */ + public Map finallyThrowParts = new HashMap<>(); + //switchedPart -> index of nextpart public Map defaultWays = new HashMap<>(); @@ -99,6 +109,8 @@ public class AVM2LocalData extends BaseLocalData { public Set finallyIndicesWithDoublePush = new HashSet<>(); + public boolean inGetLoops = false; + public AVM2LocalData() { } @@ -139,6 +151,9 @@ public class AVM2LocalData extends BaseLocalData { finallyIndicesWithDoublePush = localData.finallyIndicesWithDoublePush; finallyJumpsToFinallyIndex = localData.finallyJumpsToFinallyIndex; finallyIndexToDefaultGraphPart = localData.finallyIndexToDefaultGraphPart; + defaultParts = localData.defaultParts; + finallyThrowParts = localData.finallyThrowParts; + inGetLoops = localData.inGetLoops; } public AVM2ConstantPool getConstants() { 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 ddc63f3d7..41e11459a 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 @@ -154,16 +154,28 @@ public class AVM2Graph extends Graph { } @Override - protected void beforePrintGraph(BaseLocalData localData, String path, Set allParts, List loops) throws InterruptedException { + protected void beforeGetLoops(BaseLocalData localData, String path, Set allParts) throws InterruptedException { AVM2LocalData avm2LocalData = ((AVM2LocalData) localData); avm2LocalData.codeStats = avm2LocalData.code.getStats(avm2LocalData.abc, avm2LocalData.methodBody, avm2LocalData.methodBody.init_scope_depth, false); getIgnoredSwitches((AVM2LocalData) localData, allParts); Set integerSwitchesIps = new HashSet<>(); - for (GraphPart p : ((AVM2LocalData) localData).ignoredSwitches.values()) { + for (GraphPart p : avm2LocalData.ignoredSwitches.values()) { integerSwitchesIps.add(p.end); } + + for (GraphPart finallySwitchTarget : avm2LocalData.finallyJumps.values()) { + if (!avm2LocalData.defaultParts.values().contains(finallySwitchTarget)) { + finallySwitchTarget.throwParts.clear(); // having throwparts in these causes problems + } + } Map> setLocalPosToGetLocalPos = calculateLocalRegsUsage(avm2LocalData, integerSwitchesIps, path, allParts); avm2LocalData.setLocalPosToGetLocalPos = setLocalPosToGetLocalPos; + avm2LocalData.inGetLoops = true; + } + + @Override + protected void afterGetLoops(BaseLocalData localData, String path, Set allParts) throws InterruptedException { + ((AVM2LocalData) localData).inGetLoops = false; } private void getIgnoredSwitches(AVM2LocalData localData, Set allParts) throws InterruptedException { @@ -199,12 +211,20 @@ public class AVM2Graph extends Graph { int switchedReg = -1; int finallyKind = FINALLY_KIND_UNKNOWN; + Integer finallyThrowPushByte = null; if (finallyTryTargetStack.size() == 1) { finallyKind = FINALLY_KIND_STACK_BASED; + if (finallyTryTargetStack.peek() instanceof IntegerValueAVM2Item) { + finallyThrowPushByte = ((IntegerValueAVM2Item) finallyTryTargetStack.peek()).intValue(); + } } else if (targetOutput.size() >= 2 && (targetOutput.get(targetOutput.size() - 1) instanceof SetLocalAVM2Item) && (targetOutput.get(targetOutput.size() - 2) instanceof SetLocalAVM2Item)) { - switchedReg = ((SetLocalAVM2Item) targetOutput.get(targetOutput.size() - 1)).regIndex; + SetLocalAVM2Item setLocal = ((SetLocalAVM2Item) targetOutput.get(targetOutput.size() - 1)); + switchedReg = setLocal.regIndex; + if (setLocal.value instanceof IntegerValueAVM2Item) { + finallyThrowPushByte = ((IntegerValueAVM2Item) setLocal.value).intValue(); + } finallyKind = FINALLY_KIND_REGISTER_BASED; } else if (!targetOutput.isEmpty() && (targetOutput.get(targetOutput.size() - 1) instanceof ThrowAVM2Item)) { //inlined to single part @@ -213,7 +233,7 @@ public class AVM2Graph extends Graph { } else { //probably inlined code in more parts, cannot do :-( } - + Integer defaultPushByte = null; GraphPart switchPart = null; if (finallyKind == FINALLY_KIND_STACK_BASED) { /* @@ -259,6 +279,40 @@ public class AVM2Graph extends Graph { } } + + int finEndIp = avm2code.adr2pos(ex.end); + GraphPart finallyEndPart = searchPart(finEndIp, allParts); + List refs = getRealRefs(finallyEndPart); + + if (refs.size() == 1) { + GraphPart prev = refs.get(0); + if (prev.getHeight() == 1) { + if (avm2code.code.get(prev.start).definition instanceof PushByteIns) { + defaultPushByte = avm2code.code.get(prev.start).operands[0]; + } + } + } + if (defaultPushByte == null) { + if (getRealRefs(finallyEndPart).size() == 0) { + if (avm2code.code.get(finallyEndPart.start - 1).definition instanceof JumpIns) { + GraphPart prevPart = searchPart(finallyEndPart.start - 1, allParts); + finallyEndPart = prevPart.nextParts.get(0); + if (finallyEndPart.nextParts.size() == 1 && finallyEndPart.nextParts.get(0).refs.size() > 1) { + for (int j = finallyEndPart.start; j <= finallyEndPart.end; j++) { + AVM2Instruction ins = avm2code.code.get(j); + if (ins.definition instanceof NopIns) { + + } else if (ins.definition instanceof PushByteIns) { + defaultPushByte = ins.operands[0]; + break; + } else { + break; + } + } + } + } + } + } } else if (finallyKind == FINALLY_KIND_REGISTER_BASED) { switchPart = findLookupSwitchWithGetLocal(switchedReg, finallyPart); int startIp = code.adr2pos(ex.start); @@ -270,12 +324,7 @@ public class AVM2Graph extends Graph { } } if (tryPart != null) { - List tryPartRefs = new ArrayList<>(tryPart.refs); - for (int i = tryPartRefs.size() - 1; i >= 0; i--) { - if (tryPartRefs.get(i).start < 0) { - tryPartRefs.remove(i); - } - } + List tryPartRefs = getRealRefs(tryPart); if (tryPartRefs.size() == 1) { GraphPart beforeTryPart = tryPartRefs.get(0); if (beforeTryPart.getHeight() > 2) { @@ -284,8 +333,7 @@ public class AVM2Graph extends Graph { if (setLocalRegister == switchedReg) { if (avm2code.code.get(beforeTryPart.end - 1).definition instanceof PushByteIns) { if (switchPart != null) { - int defaultWay = avm2code.code.get(beforeTryPart.end - 1).operands[0]; - localData.defaultWays.put(switchPart, defaultWay); + defaultPushByte = avm2code.code.get(beforeTryPart.end - 1).operands[0]; } } } @@ -296,6 +344,29 @@ public class AVM2Graph extends Graph { } localData.switchedRegs.put(e, switchedReg); if (switchPart != null) { + + localData.defaultWays.put(switchPart, defaultPushByte); + + if (defaultPushByte != null) { + GraphPart defaultPart; + if (defaultPushByte == null || defaultPushByte < 0 || defaultPushByte > switchPart.nextParts.size() - 2) { + defaultPart = switchPart.nextParts.get(0); + } else { + defaultPart = switchPart.nextParts.get(1 + defaultPushByte); + } + localData.defaultParts.put(e, defaultPart); + } + + if (finallyThrowPushByte != null) { + GraphPart finnalyThrowPart; + if (finallyThrowPushByte == null || finallyThrowPushByte < 0 || finallyThrowPushByte > switchPart.nextParts.size() - 2) { + finnalyThrowPart = switchPart.nextParts.get(0); + } else { + finnalyThrowPart = switchPart.nextParts.get(1 + finallyThrowPushByte); + } + localData.finallyThrowParts.put(e, finnalyThrowPart); + } + for (GraphPart r : finallyPart.refs) { GraphPart rr = r; boolean needsPrev = true; @@ -517,23 +588,17 @@ public class AVM2Graph extends Graph { @Override protected void checkGraph(List allBlocks) { for (ABCException ex : body.exceptions) { - /*int startAddr = avm2code.adr2pos(ex.start); - int endAddr = avm2code.adr2pos(ex.end); - int targetIp = avm2code.adr2pos(ex.target);*/ - GraphPart target = null; + GraphPart targetPart = searchPart(avm2code.adr2pos(ex.target), allBlocks); for (GraphPart p : allBlocks) { - if (avm2code.pos2adr(p.start) == ex.target) { - target = p; - break; - } - } - for (GraphPart p : allBlocks) { - if (avm2code.pos2adr(p.start) >= ex.start && avm2code.pos2adr(p.end) <= ex.end && target != null) { + if (avm2code.pos2adr(p.start) >= ex.start && avm2code.pos2adr(p.end) <= ex.end && targetPart != null) { //Logger.getLogger(Graph.class.getName()).fine("ADDING throwpart " + target + " to " + p); - //p.throwParts.add(target); + p.throwParts.add(targetPart); //target.refs.add(p); } } + /*GraphPart startPart = searchPart(avm2code.adr2pos(ex.start), allBlocks); + + startPart.throwParts.add(targetPart);*/ } } @@ -648,6 +713,7 @@ public class AVM2Graph extends Graph { int endIp = -1; List finnalysIndicesToBe = new ArrayList<>(); + int realIp = -1; for (int e = 0; e < body.exceptions.length; e++) { if (addr == avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].start)) { ABCException ex = body.exceptions[e]; @@ -660,6 +726,7 @@ public class AVM2Graph extends Graph { catchedExceptions.clear(); maxEndAddr = avm2code.getAddrThroughJumpAndDebugLine(body.exceptions[e].end); endIp = avm2code.adr2pos(maxEndAddr); + realIp = avm2code.adr2pos(body.exceptions[e].end); catchedExceptions.add(body.exceptions[e]); } else if (endAddr == maxEndAddr) { catchedExceptions.add(body.exceptions[e]); @@ -672,7 +739,6 @@ public class AVM2Graph extends Graph { //GraphPart endPart = searchPart(endIp, allParts); int finallyIndex = -1; ABCException finallyException = null; - Integer defaultPushByte = null; for (int e : finnalysIndicesToBe) { ABCException finallyExceptionToBe = body.exceptions[e]; @@ -693,7 +759,6 @@ public class AVM2Graph extends Graph { if (endPart.getHeight() == 1) { if (avm2code.code.get(endPart.start).definition instanceof PushByteIns) { - /*defaultPushByte = avm2code.code.get(endPart.start).operands[0];*/ int afterEndIp = avm2code.getIpThroughJumpAndDebugLine(endPart.nextParts.get(0).start); int afterFinEndIp = avm2code.getIpThroughJumpAndDebugLine(finEndPart.start); if (afterEndIp == afterFinEndIp) { @@ -706,39 +771,6 @@ public class AVM2Graph extends Graph { } if (finallyException != null) { - int finEndIp = avm2code.adr2pos(finallyException.end); - GraphPart finallyEndPart = searchPart(finEndIp, allParts); - List refs = getRealRefs(finallyEndPart); - if (refs.size() == 1) { - GraphPart prev = refs.get(0); - if (prev.getHeight() == 1) { - if (avm2code.code.get(prev.start).definition instanceof PushByteIns) { - defaultPushByte = avm2code.code.get(prev.start).operands[0]; - } - } - } - if (defaultPushByte == null) { - if (getRealRefs(finallyEndPart).size() == 0) { - if (avm2code.code.get(finallyEndPart.start - 1).definition instanceof JumpIns) { - GraphPart prevPart = searchPart(finallyEndPart.start - 1, allParts); - finallyEndPart = prevPart.nextParts.get(0); - if (finallyEndPart.nextParts.size() == 1 && finallyEndPart.nextParts.get(0).refs.size() > 1) { - for (int j = finallyEndPart.start; j <= finallyEndPart.end; j++) { - AVM2Instruction ins = avm2code.code.get(j); - if (ins.definition instanceof NopIns) { - - } else if (ins.definition instanceof PushByteIns) { - defaultPushByte = ins.operands[0]; - break; - } else { - break; - } - } - } - } - } - } - catchedExceptions.add(finallyException); } @@ -766,22 +798,19 @@ public class AVM2Graph extends Graph { } } - afterPart = endIpPart; + GraphPart realEndIpPart = searchPart(realIp, allParts); + if (realEndIpPart != null && getRealRefs(realEndIpPart).isEmpty()) { //swftools - there is jump on previous ip + if (avm2code.code.get(realEndIpPart.start - 1).definition instanceof JumpIns) { + GraphPart prevPart = searchPart(realEndIpPart.start - 1, allParts); + realEndIpPart = prevPart.nextParts.get(0); + } + } + afterPart = realEndIpPart; + + GraphPart exAfterPart = endIpPart; stack.clear(); //If the original code (before check()) had "if" in it, there would be something on stack - if (switchedReg > -1) { - //There is assignment to switched reg before entering try - if (!currentRet.isEmpty() && (currentRet.get(currentRet.size() - 1) instanceof SetLocalAVM2Item)) { - SetLocalAVM2Item setLocal = (SetLocalAVM2Item) currentRet.get(currentRet.size() - 1); - if (setLocal.regIndex == switchedReg) { - if (setLocal.value.getNotCoerced() instanceof IntegerValueAVM2Item) { - defaultPushByte = (int) (long) ((IntegerValueAVM2Item) setLocal.value.getNotCoerced()).value; - } - currentRet.remove(currentRet.size() - 1); - } - } - } if (finallyException == null) { List stopPart2 = new ArrayList<>(stopPart); @@ -792,20 +821,14 @@ public class AVM2Graph extends Graph { boolean inlinedFinally = false; List finallyTargetItems = new ArrayList<>(); - GraphPart exAfterPart = afterPart; + GraphPart defaultPart = null; + GraphPart finallyPart = null; if (finallyException != null) { - if (defaultPushByte != null) { - GraphPart switchPart = localData.ignoredSwitches.get(finallyIndex); - if (switchPart != null) { - if (defaultPushByte < 0 || defaultPushByte > switchPart.nextParts.size() - 2) { - defaultPart = switchPart.nextParts.get(0); - } else { - defaultPart = switchPart.nextParts.get(1 + defaultPushByte); - } - localData.defaultWays.put(switchPart, defaultPushByte); - } + GraphPart switchPart = localData.ignoredSwitches.get(finallyIndex); + if (switchPart != null) { + defaultPart = localData.defaultParts.containsKey(finallyIndex) ? localData.defaultParts.get(finallyIndex) : null; } localData.finallyIndexToDefaultGraphPart.put(finallyIndex, defaultPart); @@ -820,7 +843,7 @@ public class AVM2Graph extends Graph { } } - GraphPart finallyPart = finallyTryTargetPart.nextParts.isEmpty() ? null : finallyTryTargetPart.nextParts.get(0); + finallyPart = finallyTryTargetPart.nextParts.isEmpty() ? null : finallyTryTargetPart.nextParts.get(0); List finallyTargetStopPart = new ArrayList<>(stopPart); if (endIpPart != null) { @@ -886,7 +909,6 @@ public class AVM2Graph extends Graph { } List finallyStopPart = new ArrayList<>(stopPart); - GraphPart switchPart = localData.ignoredSwitches.containsKey(finallyIndex) ? localData.ignoredSwitches.get(finallyIndex) : null; if (switchPart != null) { finallyStopPart.add(switchPart); } @@ -896,16 +918,7 @@ public class AVM2Graph extends Graph { if (switchPart != null) { finallyCommands.addAll(translatePart(localData, switchPart, stack, staticOperation, path)); stack.pop(); //value switched by lookupswitch - if (localData.defaultWays.containsKey(switchPart)) { - int defaultWay = localData.defaultWays.get(switchPart); - if (defaultWay < 0 || defaultWay > switchPart.nextParts.size() - 2) { - afterPart = switchPart.nextParts.get(0); - } else { - afterPart = switchPart.nextParts.get(1 + defaultWay); - } - } else { - afterPart = switchPart.nextParts.get(0); //take the default branch. TODO: detect actual value - } + afterPart = defaultPart; exAfterPart = afterPart; } //stack.pop(); @@ -952,6 +965,13 @@ public class AVM2Graph extends Graph { return null; } + //remove default assignment to switched register + if (switchedReg != -1 && !currentRet.isEmpty() + && (currentRet.get(currentRet.size() - 1) instanceof SetLocalAVM2Item) + && (((SetLocalAVM2Item) currentRet.get(currentRet.size() - 1)).regIndex == switchedReg)) { + currentRet.remove(currentRet.size() - 1); + } + List ret = new ArrayList<>(); if (!inlinedFinally && catchedExceptions.isEmpty() && finallyCommands.isEmpty()) { ret.addAll(tryCommands); @@ -981,7 +1001,6 @@ public class AVM2Graph extends Graph { if (finallyIndex > -1 && localData.finallyIndicesWithDoublePush.contains(finallyIndex)) { stack.push(new AnyItem()); } - ret.addAll(printGraph(foundGotos, partCodes, partCodePos, localData, stack, allParts, null, afterPart, stopPart, loops, staticOperation, path)); } return ret; @@ -1123,10 +1142,15 @@ public class AVM2Graph extends Graph { aLocalData.ignoredSwitches = new HashMap<>(); } + if (aLocalData.finallyThrowParts.containsValue(part)) { + return null; + } + if (prev != null) { - if (aLocalData.ignoredSwitches.containsValue(prev)) { + if (!aLocalData.inGetLoops && aLocalData.ignoredSwitches.containsValue(prev)) { return null; } + if (aLocalData.finallyJumps.containsKey(prev)) { GraphPart switchPart = null; int switchedReg = -1; @@ -1510,7 +1534,7 @@ public class AVM2Graph extends Graph { if ((list.get(i + 1) instanceof ThrowAVM2Item) && (list.get(i + 1).value instanceof LocalRegAVM2Item) && (((LocalRegAVM2Item) list.get(i + 1).value).regIndex == ri.regIndex)) { - ThrowAVM2Item t = (ThrowAVM2Item) list.get(i + 2); + ThrowAVM2Item t = (ThrowAVM2Item) list.get(i + 1); t.value = ri.value; list.remove(i); i--; 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 d7c8f708b..1b063ca83 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 @@ -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.AppResources; @@ -867,6 +868,7 @@ public abstract class Action implements GraphSourceItem { throw ex; } catch (Exception | OutOfMemoryError | StackOverflowError ex) { + ex.printStackTrace(); convertException = ex; Throwable cause = ex.getCause(); if (ex instanceof ExecutionException && cause instanceof Exception) { 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 51d09513d..20c8fc405 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 @@ -377,7 +377,7 @@ public class ActionGraph extends Graph { */ //must go backwards to hit case 2, not case 1 for (int i = caseBodyParts.size() - 1; i >= 0; i--) { - if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops)) { + if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops, false)) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i + 1, di); caseBodyParts.add(i + 1, defaultPart); @@ -398,7 +398,7 @@ public class ActionGraph extends Graph { trace("2"); */ for (int i = 0; i < caseBodyParts.size(); i++) { - if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops)) { + if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops, false)) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i, di); caseBodyParts.add(i, defaultPart); @@ -448,7 +448,7 @@ public class ActionGraph extends Graph { GraphPart nextCase = next; if (next != null) { if (i < caseBodies.size() - 1) { - if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops)) { + if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops, false)) { currentCaseCommands.add(new BreakItem(null, localData.lineStartInstruction, currentLoop.id)); } else { nextCase = caseBodies.get(i + 1); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java index 3557fde5f..3f5add963 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/swf7/ActionTry.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.swf7; import com.jpexs.decompiler.flash.SWFInputStream; @@ -35,6 +36,9 @@ import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphSourceItemContainer; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.TranslateStack; +import com.jpexs.decompiler.graph.model.BreakItem; +import com.jpexs.decompiler.graph.model.ContinueItem; +import com.jpexs.decompiler.graph.model.ExitItem; import com.jpexs.decompiler.graph.model.IfItem; import com.jpexs.decompiler.graph.model.PopItem; import com.jpexs.decompiler.graph.model.PushItem; @@ -300,43 +304,51 @@ public class ActionTry extends Action implements GraphSourceItemContainer { catchExceptionTypes.add(co.constructor); if (body.get(pos + 1) instanceof IfItem) { IfItem ifi = (IfItem) body.get(pos + 1); - if (!ifi.onTrue.isEmpty()) { - if (ifi.onTrue.get(0) instanceof DefineLocalActionItem) { - DefineLocalActionItem dl = (DefineLocalActionItem) ifi.onTrue.get(0); - catchExceptionNames.add(dl.name); - List catchBody = new ArrayList<>(ifi.onTrue); - catchBody.remove(0); - catchCommands.add(catchBody); - if (!ifi.onFalse.isEmpty()) { - if (ifi.onFalse.get(0) instanceof PopItem) { - pos = 1; - body = ifi.onFalse; - continue loopex; - } else { - break; - } + List onFalse = ifi.onFalse; + int onFalsePos = 0; + if (ifi.onFalse.isEmpty() && !ifi.onTrue.isEmpty() && ((ifi.onTrue.get(ifi.onTrue.size() - 1) instanceof ExitItem) + || (ifi.onTrue.get(ifi.onTrue.size() - 1) instanceof BreakItem) + || (ifi.onTrue.get(ifi.onTrue.size() - 1) instanceof ContinueItem))) { + onFalse = body; + onFalsePos = pos + 2; + } + + if (!ifi.onTrue.isEmpty() && (ifi.onTrue.get(0) instanceof DefineLocalActionItem)) { + DefineLocalActionItem dl = (DefineLocalActionItem) ifi.onTrue.get(0); + catchExceptionNames.add(dl.name); + List catchBody = new ArrayList<>(ifi.onTrue); + catchBody.remove(0); + catchCommands.add(catchBody); + if (onFalse.size() > onFalsePos) { + if (onFalse.get(onFalsePos) instanceof PopItem) { + pos = onFalsePos + 1; + body = onFalse; + continue loopex; } else { break; } - /*if (body.size() == pos + 4) { - if (body.get(pos + 2) instanceof PopItem) { - if (body.get(pos + 3) instanceof ThrowActionItem) { - ThrowActionItem ta = (ThrowActionItem) body.get(pos + 3); - if (ta.value instanceof DirectValueActionItem) { - if (((DirectValueActionItem) ta.value).value instanceof RegisterNumber) { - RegisterNumber rn2 = (RegisterNumber) ((DirectValueActionItem) ta.value).value; - if (rn2.number == catchRegister) { - break; - } - } - } - } - } - }else{ - - }*/ + } else { + break; + } + } else if (onFalse.size() > onFalsePos && (onFalse.get(onFalsePos) instanceof DefineLocalActionItem)) { + DefineLocalActionItem dl = (DefineLocalActionItem) onFalse.get(onFalsePos); + catchExceptionNames.add(dl.name); + + List catchBody = new ArrayList<>(); + for (int i = onFalsePos; i < onFalse.size(); i++) { + catchBody.add(onFalse.get(i)); + } + catchBody.remove(0); + catchCommands.add(catchBody); + if (!ifi.onTrue.isEmpty()) { + if (ifi.onTrue.get(0) instanceof PopItem) { + pos = 1; + body = ifi.onTrue; + continue loopex; + } } } + } } } 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 72cdc8472..b4e7d1791 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -304,7 +304,7 @@ public class Graph { if (q == p) { continue; } - if (!q.leadsTo(localData, this, code, p, loops)) { + if (!q.leadsTo(localData, this, code, p, loops, false)) { common = false; break; } @@ -364,7 +364,7 @@ public class Graph { if (q == p) { continue; } - if (!q.leadsTo(localData, this, code, p, loops)) { + if (!q.leadsTo(localData, this, code, p, loops, false)) { common = false; break; } @@ -499,10 +499,12 @@ public class Graph { System.err.println("/parts"); } TranslateStack stack = new TranslateStack(path); + beforeGetLoops(localData, path, allParts); List loops = new ArrayList<>(); getLoops(localData, heads.get(0), loops, null); + afterGetLoops(localData, path, allParts); if (debugPrintLoopList) { System.err.println(""); for (Loop el : loops) { @@ -523,7 +525,6 @@ public class Graph { System.err.println("");//*/ List gotos = new ArrayList<>(); - beforePrintGraph(localData, path, allParts, loops); List ret = printGraph(gotos, new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path); processIfGotos(gotos, ret); @@ -555,7 +556,11 @@ public class Graph { return new FinalProcessLocalData(loops); } - protected void beforePrintGraph(BaseLocalData localData, String path, Set allParts, List loops) throws InterruptedException { + protected void beforeGetLoops(BaseLocalData localData, String path, Set allParts) throws InterruptedException { + + } + + protected void afterGetLoops(BaseLocalData localData, String path, Set allParts) throws InterruptedException { } @@ -614,7 +619,7 @@ public class Graph { el.backEdges.clear(); Set uniqueRefs = new HashSet<>(el.loopContinue.refs); for (GraphPart r : uniqueRefs) { - if (el.loopContinue.leadsTo(localData, this, code, r, loops)) { + if (el.loopContinue.leadsTo(localData, this, code, r, loops, true)) { el.backEdges.add(r); } } @@ -905,6 +910,23 @@ public class Graph { } } + //Prefer continue/return/throw/break in onTrue rather than onFalse + if (!onFalse.isEmpty() + && ((onFalse.get(onFalse.size() - 1) instanceof BreakItem) + || (onFalse.get(onFalse.size() - 1) instanceof ExitItem) + || (onFalse.get(onFalse.size() - 1) instanceof ContinueItem) ) + && !(onFalse.get(onFalse.size() - 1) instanceof ScriptEndItem) + && (onTrue.isEmpty() || !((onTrue.get(onTrue.size() - 1) instanceof BreakItem) + || (onTrue.get(onTrue.size() - 1) instanceof ExitItem) + || (onTrue.get(onTrue.size() - 1) instanceof ContinueItem)))) { + ifi.expression = ifi.expression.invert(null); + ifi.onTrue = onFalse; + ifi.onFalse = new ArrayList<>(); + list.addAll(i + 1, onTrue); + onFalse = ifi.onFalse; + onTrue = ifi.onTrue; + } + if (i < list.size() - 1) { if ((list.get(i + 1) instanceof BreakItem) && onFalse.isEmpty()) { if (!onTrue.isEmpty() && (onTrue.get(onTrue.size() - 1) instanceof ContinueItem)) { @@ -1165,7 +1187,7 @@ public class Graph { } else { List loops2 = new ArrayList<>(loops); loops2.remove(lastP1); - if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2)) { + if (!part.leadsTo(localData, this, code, lastP1.loopContinue, loops2, true)) { if (lastP1.breakCandidatesLocked == 0) { if (debugGetLoops) { System.err.println("added breakCandidate " + part + " to " + lastP1); @@ -1190,7 +1212,7 @@ public class Graph { } part.level = level; - boolean isLoop = part.leadsTo(localData, this, code, part, loops); + boolean isLoop = part.leadsTo(localData, this, code, part, loops, true); Loop currentLoop = null; if (isLoop) { currentLoop = new Loop(loops.size(), part, null); @@ -1282,7 +1304,7 @@ public class Graph { if (cand == cand2) { continue; } - if (cand.leadsTo(localData, this, code, cand2, loops)) { + if (cand.leadsTo(localData, this, code, cand2, loops, true)) { int lev1 = Integer.MAX_VALUE; int lev2 = Integer.MAX_VALUE; for (int i = 0; i < currentLoop.breakCandidates.size(); i++) { @@ -2507,7 +2529,7 @@ public class Graph { */ //must go backwards to hit case 2, not case 1 for (int i = caseBodyParts.size() - 1; i >= 0; i--) { - if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops)) { + if (caseBodyParts.get(i).leadsTo(localData, this, code, defaultPart, loops, false)) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i + 1, di); caseBodyParts.add(i + 1, defaultPart); @@ -2528,7 +2550,7 @@ public class Graph { trace("2"); */ for (int i = 0; i < caseBodyParts.size(); i++) { - if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops)) { + if (defaultPart.leadsTo(localData, this, code, caseBodyParts.get(i), loops, false)) { DefaultItem di = new DefaultItem(); caseValuesMap.add(i, di); caseBodyParts.add(i, defaultPart); @@ -2580,13 +2602,13 @@ public class Graph { GraphPart b = caseBodies.get(i); for (int j = i + 1; j < caseBodies.size(); j++) { GraphPart b2 = caseBodies.get(j); - if (b2.leadsTo(localData, this, code, b, loops)) { + if (b2.leadsTo(localData, this, code, b, loops, false)) { caseBodies.remove(j); caseBodies.add(i, b2); i--; continue loopi; } else if (j > i + 1) { - if (b.leadsTo(localData, this, code, b2, loops)) { + if (b.leadsTo(localData, this, code, b2, loops, false)) { caseBodies.remove(j); caseBodies.add(i + 1, b2); continue loopi; @@ -2603,7 +2625,7 @@ public class Graph { for (int i = 0; i < caseBodies.size(); i++) { List currentCaseCommands = new ArrayList<>(); if (i < caseBodies.size() - 1) { - if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops)) { + if (!caseBodies.get(i).leadsTo(localData, this, code, caseBodies.get(i + 1), loops, false)) { currentCaseCommands.add(new BreakItem(null, localData.lineStartInstruction, currentLoop.id)); } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java index 3cb3baec2..61f4a4f04 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.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; @@ -123,7 +124,7 @@ public class GraphPart implements Serializable { return time; } - private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet visited, List loops) throws InterruptedException { + private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet visited, List loops, boolean useThrow) throws InterruptedException { if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } @@ -133,7 +134,7 @@ public class GraphPart implements Serializable { return false; } if (tpart != this) { - return tpart.leadsTo(localData, gr, code, null, part, visited, loops); + return tpart.leadsTo(localData, gr, code, null, part, visited, loops, useThrow); } Loop currentLoop = null; for (Loop l : loops) { @@ -170,25 +171,27 @@ public class GraphPart implements Serializable { for (GraphPart p : nextParts) { if (p == part) { return true; - } else if (p.leadsTo(localData, gr, code, this, part, visited, loops)) { + } else if (p.leadsTo(localData, gr, code, this, part, visited, loops, useThrow)) { return true; } } - for (GraphPart p : throwParts) { - if (p == part) { - return true; - } else if (p.leadsTo(localData, gr, code, this, part, visited, loops)) { - return true; + if (useThrow) { + for (GraphPart p : throwParts) { + if (p == part) { + return true; + } else if (p.leadsTo(localData, gr, code, this, part, visited, loops, useThrow)) { + return true; + } } } return false; } - public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List loops) throws InterruptedException { + public boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart part, List loops, boolean useThrow) throws InterruptedException { for (Loop l : loops) { l.leadsToMark = 0; } - return leadsTo(localData, gr, code, null /*???*/, part, new HashSet<>(), loops); + return leadsTo(localData, gr, code, null /*???*/, part, new HashSet<>(), loops, useThrow); } public GraphPart(int start, int end) { diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ClassicDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ClassicDecompileTest.java index f2325d06a..49784d84a 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ClassicDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3ClassicDecompileTest.java @@ -431,14 +431,14 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes + "{\r\n" + "trace(\"A\");\r\n" + "}\r\n" - + "else if(c == 3)\r\n" - + "{\r\n" - + "trace(\"B\");\r\n" - + "}\r\n" + "else\r\n" + "{\r\n" + + "if(c != 3)\r\n" + + "{\r\n" + "continue;\r\n" + "}\r\n" + + "trace(\"B\");\r\n" + + "}\r\n" + "trace(\"C\");\r\n" + "}\r\n" + "trace(\"exit\");\r\n", @@ -480,14 +480,14 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes + "{\r\n" + "trace(\"A\");\r\n" + "}\r\n" - + "else if(c == 3)\r\n" - + "{\r\n" - + "trace(\"B\");\r\n" - + "}\r\n" + "else\r\n" + "{\r\n" + + "if(c != 3)\r\n" + + "{\r\n" + "continue;\r\n" + "}\r\n" + + "trace(\"B\");\r\n" + + "}\r\n" + "trace(\"C\");\r\n" + "}\r\n", false); @@ -1303,7 +1303,6 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes + "{\r\n" + "trace(\"a\");\r\n" + "}\r\n" - + "continue;\r\n" + "}\r\n" + "catch(e:EOFError)\r\n" + "{\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileDecompileTest.java index f2208e7d7..223369b95 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileDecompileTest.java @@ -76,6 +76,68 @@ public class ActionScript3CrossCompileDecompileTest extends ActionScript3Decompi false); } + @Test(dataProvider = "swfNamesProvider") + public void testTryCatchInWhile(String swfUsed) { + decompileMethod(swfUsed, "testTryCatchInWhile", "trace(\"before loop\");\r\n" + + "while(true)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "while(true)\r\n" + + "{\r\n" + + "trace(\"a\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "}\r\n", + false); + } + + @Test(dataProvider = "swfNamesProvider") + public void testTryCatchInWhile2(String swfUsed) { + decompileMethod(swfUsed, "testTryCatchInWhile2", "var a:int = 0;\r\n" + + "a = 0;\r\n" + + "trace(\"before loop\");\r\n" + + "while(a > 5)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "if(a == 6)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "if(a == 7)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"after inner while\");\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "if(a == 8)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "continue;\r\n" + + "}\r\n" + + "a++;\r\n" + + "}\r\n", + false); + } + @Test(dataProvider = "swfNamesProvider") public void testTryCatchLoop(String swfUsed) { decompileMethod(swfUsed, "testTryCatchLoop", "var j:int = 0;\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileSwfToolsDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileSwfToolsDecompileTest.java new file mode 100644 index 000000000..d7037390a --- /dev/null +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/ActionScript3CrossCompileSwfToolsDecompileTest.java @@ -0,0 +1,463 @@ +package com.jpexs.decompiler.flash; + +import java.io.IOException; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author JPEXS + */ +public class ActionScript3CrossCompileSwfToolsDecompileTest extends ActionScript3DecompileTestBase { + + @BeforeClass + public void init() throws IOException, InterruptedException { + addSwf("swftools", "testdata/cross_compile/bin/Main.swftools.swf"); + } + + @Test + public void testTryCatch() { + decompileMethod("swftools", "testTryCatch", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "trace(\"after\");\r\n", + false); + } + + @Test + public void testTryCatchExceptionUsage() { + decompileMethod("swftools", "testTryCatchExceptionUsage", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "var _loc1_:* = e;\r\n" + + "trace(\"catched exception: \" + _loc1_.message);\r\n" + + "}\r\n" + + "trace(\"after\");\r\n", + false); + } + + @Test + public void testTryCatchIfInTry() { + decompileMethod("swftools", "testTryCatchIfInTry", "var _loc1_:Boolean = true;\r\n" + + "trace(\"before\");\r\n" + + "try\r\n" + + "{\r\n" + + "if(_loc1_)\r\n" + + "{\r\n" + + "trace(\"ret\");\r\n" + + "return;\r\n" + + "}\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "trace(\"after\");\r\n", + false); + } + + @Test + public void testTryCatchInWhile() { + decompileMethod("swftools", "testTryCatchInWhile", "trace(\"before loop\");\r\n" + + "while(true)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "while(true)\r\n" + + "{\r\n" + + "trace(\"a\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryCatchInWhile2() { + decompileMethod("swftools", "testTryCatchInWhile2", "var _loc1_:int = 0;\r\n" + + "_loc1_ = 0;\r\n" + + "trace(\"before loop\");\r\n" + + "while(_loc1_ > 5)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "if(_loc1_ == 6)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "if(_loc1_ == 7)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"after inner while\");\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "if(_loc1_ == 8)\r\n" + + "{\r\n" + + "break;\r\n" + + "}\r\n" + + "continue;\r\n" + + "}\r\n" + + "_loc1_++;\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryCatchLoop() { + decompileMethod("swftools", "testTryCatchLoop", "var _loc1_:int = 0;\r\n" + + "while(_loc1_ < 100)\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "var _loc2_:int = 0;\r\n" + + "while(_loc2_ < 20)\r\n" + + "{\r\n" + + "trace(\"a\");\r\n" + + "_loc2_++;\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"after_try\");\r\n" + + "_loc1_++;\r\n" + + "}\r\n" + + "trace(\"end\");\r\n", + false); + } + + @Test + public void testTryFinally() { + decompileMethod("swftools", "testTryFinally", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n" + + "trace(\"in finally\");\r\n" + + "trace(\"after\");\r\n", + false); + } + + @Test + public void testTryFinallyDirectReturnInFinally() { + decompileMethod("swftools", "testTryFinallyDirectReturnInFinally", "var _loc1_:String = \"xxx\";\r\n" + + "try\r\n" + + "{\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"error\");\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"hi \");\r\n" + + "if(_loc1_ == \"check\")\r\n" + + "{\r\n" + + "return _loc1_;\r\n" + + "}\r\n" + + "return \"hu\" + _loc1_;\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryFinallyLoop() { + decompileMethod("swftools", "testTryFinallyLoop", "var _loc1_:* = 0;\r\n" + + "while(_loc1_ < 10)\r\n" + + "{\r\n" + + "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "if(_loc1_ == 5)\r\n" + + "{\r\n" + + "_loc1_ = _loc1_ + 5;\r\n" + + "trace(\"continue while\");\r\n" + + "trace(\"in finally\");\r\n" + + "continue;\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n" + + "trace(\"in finally\");\r\n" + + "trace(\"after\");\r\n" + + "_loc1_++;\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryFinallyLoopInFinally() { + decompileMethod("swftools", "testTryFinallyLoopInFinally", "var _loc1_:* = 0;\r\n" + + "while(_loc1_ < 10)\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" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "if(_loc1_ == 5)\r\n" + + "{\r\n" + + "_loc1_ = _loc1_ + 7;\r\n" + + "trace(\"continue while\");\r\n" + + "continue;\r\n" + + "}\r\n" + + "trace(\"in finally\");\r\n" + + "}\r\n" + + "trace(\"after\");\r\n" + + "_loc1_++;\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryFinallyMultipleCatch() { + decompileMethod("swftools", "testTryFinallyMultipleCatch", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch Error\");\r\n" + + "}\r\n" + + "catch(e:EOFError)\r\n" + + "{\r\n" + + "trace(\"in catch EOFError\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n" + + "trace(\"in finally\");\r\n" + + "trace(\"after\");\r\n", + false); + } + + @Test + public void testTryFinallyNoCatch() { + decompileMethod("swftools", "testTryFinallyNoCatch", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "trace(\"in finally\");\r\n" + + "trace(\"after\");\r\n" + + "return;\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryFinallyReturn() { + decompileMethod("swftools", "testTryFinallyReturn", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "var _loc1_:int = 5;\r\n" + + "if(_loc1_ > 4)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "return \"RET\";\r\n" + + "}\r\n" + + "trace(\"between\");\r\n" + + "if(_loc1_ < 3)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "return \"RE2\";\r\n" + + "}\r\n" + + "trace(\"in try2\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n" + + "trace(\"in finally\");\r\n" + + "trace(\"after\");\r\n" + + "return \"RETFINAL\";\r\n", + false); + } + + @Test + public void testTryFinallyReturnInFinally() { + decompileMethod("swftools", "testTryFinallyReturnInFinally", "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "var _loc1_:int = 5;\r\n" + + "if(_loc1_ > 4)\r\n" + + "{\r\n" + + "return \"RET\";\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "finally\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "if(_loc1_ > 6)\r\n" + + "{\r\n" + + "return \"FINRET1\";\r\n" + + "}\r\n" + + "trace(\"xx\");\r\n" + + "if(_loc1_ > 5)\r\n" + + "{\r\n" + + "return \"FINRET2\";\r\n" + + "}\r\n" + + "trace(\"nofinret\");\r\n" + + "}\r\n" + + "trace(\"after\");\r\n" + + "return \"RETEXIT\";\r\n", + false); + } + + @Test + public void testTryFinallyReturnNested() { + decompileMethod("swftools", "testTryFinallyReturnNested", "var _loc1_:int = Math.random() * 5;\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"before try2\");\r\n" + + "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try2\");\r\n" + + "if(_loc1_ > 4)\r\n" + + "{\r\n" + + "trace(\"in finally2\");\r\n" + + "trace(\"in finally1\");\r\n" + + "return \"RET\";\r\n" + + "}\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally2\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n" + + "trace(\"in finally2\");\r\n" + + "trace(\"after\");\r\n" + + "trace(\"in finally1\");\r\n" + + "return \"RETFINAL\";\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally1\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n", + false); + } + + @Test + public void testTryFinallyReturnVoid() { + decompileMethod("swftools", "testTryFinallyReturnVoid", "var _loc1_:int = Math.random() * 5;\r\n" + + "trace(\"before try\");\r\n" + + "try\r\n" + + "{\r\n" + + "try\r\n" + + "{\r\n" + + "trace(\"in try\");\r\n" + + "if(_loc1_ > 4)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "return;\r\n" + + "}\r\n" + + "trace(\"in try2\");\r\n" + + "}\r\n" + + "catch(e:Error)\r\n" + + "{\r\n" + + "trace(\"in catch\");\r\n" + + "}\r\n" + + "}\r\n" + + "catch(_loc_e_:*)\r\n" + + "{\r\n" + + "trace(\"in finally\");\r\n" + + "throw _loc_e_;\r\n" + + "}\r\n" + + "trace(\"in finally\");\r\n" + + "trace(\"after\");\r\n", + false); + } +} diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java index 6c220e782..23436a619 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/generators/AS3Generator.java @@ -175,6 +175,8 @@ public class AS3Generator { {"testdata/cross_compile/bin/Main.flex.swf", "flex"}, {"testdata/cross_compile/bin/Main.air.swf", "air"} }, true); + useFile("ActionScript3CrossCompileSwfToolsDecompileTest", new String[][]{ + {"testdata/cross_compile/bin/Main.swftools.swf", "swftools"},}, false); useFile("ActionScript3AssembledDecompileTest", new String[][]{{"testdata/custom/bin/custom.swf", "assembled"}}, false); System.exit(0); diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf index f7d505c7caa8a84e655c8a634edc89e15915db6b..32af5843655bbb1437472e176746bf3662b9f2f2 100644 GIT binary patch literal 3154 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 literal 2872 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 diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf index 382c364051880cbb7b9adde36d704686f3d6d0d4..ac746e768838f66ac5deebe332e3ba7d9775f313 100644 GIT binary patch literal 4317 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 L2S5G~GsGhwUQ|;| literal 3846 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 id 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 diff --git a/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old index 90790e11c..55f0c7a30 100644 --- a/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old +++ b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.old @@ -16,7 +16,7 @@ CONFIG::timeStamp - '31.01.2021' + '02.02.2021' CONFIG::air diff --git a/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml index 90790e11c..55f0c7a30 100644 --- a/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml +++ b/libsrc/ffdec_lib/testdata/cross_compile/obj/cross_compileConfig.xml @@ -16,7 +16,7 @@ CONFIG::timeStamp - '31.01.2021' + '02.02.2021' CONFIG::air diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as b/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as index d980c14da..3c4e427c9 100644 --- a/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/Main.as @@ -12,6 +12,8 @@ package { TestTryCatch; TestTryCatchIfInTry; + TestTryCatchInWhile; + TestTryCatchInWhile2; TestTryCatchLoop; TestTryCatchExceptionUsage TestTryFinally; diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile.as new file mode 100644 index 000000000..893c3a80f --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile.as @@ -0,0 +1,41 @@ +package tests +{ + import flash.errors.EOFError; + /** + * ... + * @author JPEXS + */ + public class TestTryCatchInWhile + { + + public function run() : void + { + trace("before loop"); + while (true) + { + try + { + trace("in try"); + while (true) + { + trace("a"); + } + //not reachable in ASC2 + //trace("after inner while"); + } + catch (e:EOFError) + { + continue; + } + catch (e:Error) + { + continue; + } + } + //not reachable in ASC2: + //trace("after loop"); + } + + } + +} \ No newline at end of file diff --git a/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile2.as b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile2.as new file mode 100644 index 000000000..348a44426 --- /dev/null +++ b/libsrc/ffdec_lib/testdata/cross_compile/src/tests/TestTryCatchInWhile2.as @@ -0,0 +1,48 @@ +package tests +{ + import flash.errors.EOFError; + /** + * ... + * @author JPEXS + */ + public class TestTryCatchInWhile2 + { + + public function run() : void + { + var a:int; + a = 0; + trace("before loop"); + while (a > 5) + { + try + { + trace("in try"); + if (a == 6){ + continue; + } + if (a == 7){ + break; + } + trace("after inner while"); + } + catch (e:EOFError) + { + continue; + } + catch (e:Error) + { + if (a == 8){ + break; + } + continue; + } + a++; + } + //not reachable in ASC2: + //trace("after loop"); + } + + } + +} \ No newline at end of file