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 f7d505c7c..32af58436 100644 Binary files a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.air.swf differ 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 382c36405..ac746e768 100644 Binary files a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf index 97dbd7b75..a514cdfd3 100644 Binary files a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.flex_apache.swf differ diff --git a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf index 1fceba1de..8c479fc34 100644 Binary files a/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf and b/libsrc/ffdec_lib/testdata/cross_compile/bin/Main.swftools.swf differ 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