diff --git a/CHANGELOG.md b/CHANGELOG.md index 03526bb0c..4cc4e3174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ All notable changes to this project will be documented in this file. - AS3 - Avoid Error Implicit coercion of a value of type XXX to an unrelated type YYY - AS3 - XML - get descendants operator parenthesis - Switch decompilation in some corner cases +- [#1894] Switches vs loops decompilation (now with two passes) ## [17.0.2] - 2022-11-22 ### Fixed @@ -2669,6 +2670,7 @@ All notable changes to this project will be documented in this file. [#1810]: https://www.free-decompiler.com/flash/issues/1810 [#1891]: https://www.free-decompiler.com/flash/issues/1891 [#1892]: https://www.free-decompiler.com/flash/issues/1892 +[#1894]: https://www.free-decompiler.com/flash/issues/1894 [#1882]: https://www.free-decompiler.com/flash/issues/1882 [#1880]: https://www.free-decompiler.com/flash/issues/1880 [#1881]: https://www.free-decompiler.com/flash/issues/1881 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/BaseLocalData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/BaseLocalData.java index 506438f43..c5b1a1e75 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/BaseLocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/BaseLocalData.java @@ -16,8 +16,11 @@ */ package com.jpexs.decompiler.flash; +import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.SecondPassData; +import java.util.HashSet; +import java.util.Set; /** * @@ -26,6 +29,8 @@ import com.jpexs.decompiler.graph.SecondPassData; public abstract class BaseLocalData { public GraphSourceItem lineStartInstruction; - + + public Set allSwitchParts = new HashSet<>(); + public SecondPassData secondPassData = null; } 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 428880e9b..066e2aa56 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 @@ -142,6 +142,7 @@ public class AVM2LocalData extends BaseLocalData { } public AVM2LocalData(AVM2LocalData localData) { + allSwitchParts = localData.allSwitchParts; isStatic = localData.isStatic; classIndex = localData.classIndex; localRegs = localData.localRegs; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java index 6d63513db..afc22356a 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/AVM2Code.java @@ -298,6 +298,7 @@ import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.SecondPassException; import com.jpexs.decompiler.graph.SimpleValue; import com.jpexs.decompiler.graph.TranslateStack; import com.jpexs.decompiler.graph.TypeItem; @@ -1589,7 +1590,7 @@ public class AVM2Code implements Cloneable { return pos2adr(getIpThroughJumpAndDebugLine(adr2pos(addr, true))); } - public ConvertOutput toSourceOutput(List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, HashMap> refs) throws ConvertException, InterruptedException { + public ConvertOutput toSourceOutput(Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssigmentIps, HashMap> refs) throws ConvertException, InterruptedException { calcKilledStats(body); boolean debugMode = DEBUG_MODE; if (debugMode) { @@ -1664,7 +1665,7 @@ public class AVM2Code implements Cloneable { do { AVM2Instruction insAfter = ip + 1 < code.size() ? code.get(ip + 1) : null; if (insAfter == null) { - ins.definition.translate(callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); ip++; break; } @@ -1686,14 +1687,14 @@ public class AVM2Code implements Cloneable { //stack.add("(" + stack.pop() + ")||"); isAnd = false; } else { - ins.definition.translate(callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); ip++; break; //throw new ConvertException("Unknown pattern after DUP:" + insComparsion.toString()); } } while (ins.definition instanceof DupIns); } else if ((ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ThrowIns)) { - ins.definition.translate(callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); //ip = end + 1; break; } else if (ins.definition instanceof NewFunctionIns) { @@ -1729,13 +1730,13 @@ public class AVM2Code implements Cloneable { } } // What to do when hasDup is false? - ins.definition.translate(callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); NewFunctionAVM2Item nft = (NewFunctionAVM2Item) stack.peek(); nft.functionName = functionName; ip++; } else { try { - ins.definition.translate(callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); + ins.definition.translate(switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssigmentIps, ip, refs, this, thisHasDefaultToPrimitive); } catch (RuntimeException re) { /*String last=""; int len=5; @@ -2150,8 +2151,11 @@ public class AVM2Code implements Cloneable { } //try { - list = AVM2Graph.translateViaGraph(callStack, abcIndex, path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, localRegTypes, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs, thisHasDefaultToPrimitive); - + try{ + list = AVM2Graph.translateViaGraph(null, callStack, abcIndex, path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, localRegTypes, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs, thisHasDefaultToPrimitive); + } catch(SecondPassException spe) { + list = AVM2Graph.translateViaGraph(spe.getData(), callStack, abcIndex, path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, localRegTypes, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs, thisHasDefaultToPrimitive); + } if (initTraits != null) { loopi: for (int i = 0; i < list.size(); i++) { 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 25fee77bc..637333a29 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 @@ -101,6 +101,7 @@ import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.GraphTargetVisitorInterface; import com.jpexs.decompiler.graph.Loop; import com.jpexs.decompiler.graph.ScopeStack; +import com.jpexs.decompiler.graph.SecondPassData; import com.jpexs.decompiler.graph.StopPartKind; import com.jpexs.decompiler.graph.ThrowState; import com.jpexs.decompiler.graph.TranslateStack; @@ -633,10 +634,11 @@ public class AVM2Graph extends Graph { return setLocalPosToGetLocalPos; } - public static List translateViaGraph(List callStack, AbcIndexing abcIndex, String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, int staticOperation, HashMap localRegAssigmentIps, HashMap> refs, boolean thisHasDefaultToPrimitive) throws InterruptedException { + public static List translateViaGraph(SecondPassData secondPassData, List callStack, AbcIndexing abcIndex, String path, AVM2Code code, ABC abc, MethodBody body, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, ScopeStack scopeStack, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, int staticOperation, HashMap localRegAssigmentIps, HashMap> refs, boolean thisHasDefaultToPrimitive) throws InterruptedException { AVM2Graph g = new AVM2Graph(abcIndex, code, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, fullyQualifiedNames, localRegAssigmentIps, refs); AVM2LocalData localData = new AVM2LocalData(); + localData.secondPassData = secondPassData; localData.thisHasDefaultToPrimitive = thisHasDefaultToPrimitive; localData.isStatic = isStatic; localData.classIndex = classIndex; @@ -657,7 +659,7 @@ public class AVM2Graph extends Graph { Set allParts = new HashSet<>(); for (GraphPart head : g.heads) { populateParts(head, allParts); - } + } return g.translate(localData, staticOperation, path); } @@ -2424,4 +2426,8 @@ public class AVM2Graph extends Graph { Graph.makeAllCommands(commands, stack); } + @Override + protected SecondPassData prepareSecondPass(List list) { + return new SecondPassData(); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java index 94049e433..18506d38c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/graph/AVM2GraphSource.java @@ -119,7 +119,7 @@ public class AVM2GraphSource extends GraphSource { List ret = new ArrayList<>(); ScopeStack newstack = ((AVM2LocalData) localData).scopeStack; Reference lineStartItem = new Reference<>(localData.lineStartInstruction); - ConvertOutput co = code.toSourceOutput(((AVM2LocalData) localData).callStack, ((AVM2LocalData) localData).abcIndex, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).thisHasDefaultToPrimitive, lineStartItem, path, part, false, isStatic, scriptIndex, classIndex, localRegs, stack, newstack, abc, body, start, end, localRegNames, ((AVM2LocalData) localData).localRegTypes, fullyQualifiedNames, new boolean[size()], localRegAssigmentIps, refs); + ConvertOutput co = code.toSourceOutput(localData.allSwitchParts, ((AVM2LocalData) localData).callStack, ((AVM2LocalData) localData).abcIndex, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).thisHasDefaultToPrimitive, lineStartItem, path, part, false, isStatic, scriptIndex, classIndex, localRegs, stack, newstack, abc, body, start, end, localRegNames, ((AVM2LocalData) localData).localRegTypes, fullyQualifiedNames, new boolean[size()], localRegAssigmentIps, refs); localData.lineStartInstruction = lineStartItem.getVal(); ret.addAll(co.output); return ret; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java index 088c11742..20305be06 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/abc/avm2/instructions/InstructionDefinition.java @@ -44,6 +44,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.TraitWithSlot; import com.jpexs.decompiler.flash.abc.types.traits.Traits; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; import com.jpexs.decompiler.graph.DottedChain; +import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.ScopeStack; @@ -171,8 +172,9 @@ public abstract class InstructionDefinition implements Serializable { public void translate(AVM2LocalData localData, TranslateStack stack, AVM2Instruction ins, List output, String path) throws InterruptedException { } - public void translate(List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, Reference lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2Instruction ins, List output, MethodBody body, ABC abc, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code, boolean thisHasDefaultToPrimitive) throws InterruptedException { + public void translate(Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, Reference lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, AVM2Instruction ins, List output, MethodBody body, ABC abc, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, HashMap> refs, AVM2Code code, boolean thisHasDefaultToPrimitive) throws InterruptedException { AVM2LocalData localData = new AVM2LocalData(); + localData.allSwitchParts = switchParts; localData.isStatic = isStatic; localData.scriptIndex = scriptIndex; localData.classIndex = classIndex; 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 3cab5b579..51a5529ac 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 @@ -63,6 +63,7 @@ import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.tags.DoInitActionTag; import com.jpexs.decompiler.flash.tags.base.ASMSource; import com.jpexs.decompiler.graph.Graph; +import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphPartChangeException; import com.jpexs.decompiler.graph.GraphSource; import com.jpexs.decompiler.graph.GraphSourceItem; @@ -1009,11 +1010,11 @@ public abstract class Action implements GraphSourceItem { return variables2; } - public static List actionsPartToTree(SecondPassData secondPassData, boolean insideDoInitAction, Reference fi, HashMap registerNames, HashMap variables, HashMap functions, TranslateStack stack, List actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException { + public static List actionsPartToTree(Set switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference fi, HashMap registerNames, HashMap variables, HashMap functions, TranslateStack stack, List actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException { if (start < actions.size() && (end > 0) && (start > 0)) { logger.log(Level.FINE, "Entering {0}-{1}{2}", new Object[]{start, end, actions.size() > 0 ? (" (" + actions.get(start).toString() + " - " + actions.get(end == actions.size() ? end - 1 : end) + ")") : ""}); } - ActionLocalData localData = new ActionLocalData(secondPassData, insideDoInitAction, registerNames, variables, functions); + ActionLocalData localData = new ActionLocalData(switchParts, secondPassData, insideDoInitAction, registerNames, variables, functions); localData.lineStartAction = fi.getVal(); List output = new ArrayList<>(); int ip = start; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java index fb1c2974a..67e8bfb1b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java @@ -112,7 +112,7 @@ public class ActionGraphSource extends GraphSource { public List translatePart(GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException { Reference fi = new Reference<>(localData.lineStartInstruction); - List r = Action.actionsPartToTree(localData.secondPassData, this.insideDoInitAction, fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path, charset); + List r = Action.actionsPartToTree(localData.allSwitchParts, localData.secondPassData, this.insideDoInitAction, fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path, charset); localData.lineStartInstruction = fi.getVal(); return r; } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionLocalData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionLocalData.java index b30c91392..2e38c681e 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionLocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionLocalData.java @@ -17,10 +17,12 @@ package com.jpexs.decompiler.flash.action; import com.jpexs.decompiler.flash.BaseLocalData; +import com.jpexs.decompiler.graph.GraphPart; import com.jpexs.decompiler.graph.GraphSourceItem; import com.jpexs.decompiler.graph.GraphTargetItem; import com.jpexs.decompiler.graph.SecondPassData; import java.util.HashMap; +import java.util.Set; /** * @@ -51,10 +53,11 @@ public class ActionLocalData extends BaseLocalData { this.secondPassData = secondPassData; variables = new HashMap<>(); functions = new HashMap<>(); - this.insideDoInitAction = insideDoInitAction; + this.insideDoInitAction = insideDoInitAction; } - public ActionLocalData(SecondPassData secondPassData, boolean insideDoInitAction, HashMap regNames, HashMap variables, HashMap functions) { + public ActionLocalData(Set switchParts, SecondPassData secondPassData, boolean insideDoInitAction, HashMap regNames, HashMap variables, HashMap functions) { + this.allSwitchParts = switchParts; this.regNames = regNames; this.variables = variables; this.functions = functions; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionSecondPassData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionSecondPassData.java index ba4662c89..afc9d8654 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionSecondPassData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/ActionSecondPassData.java @@ -29,5 +29,5 @@ import java.util.List; public class ActionSecondPassData extends SecondPassData { List> switchParts = new ArrayList<>(); List> switchOnFalseParts = new ArrayList<>(); - List> switchCaseExpressions = new ArrayList<>(); + List> switchCaseExpressions = new ArrayList<>(); } 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 5809038bb..ad98a3ac1 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -61,6 +61,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.Stack; import java.util.TreeSet; @@ -116,6 +117,7 @@ public class Graph { List visited = new ArrayList<>(); for (GraphPart head : heads) { time = head.setTime(time, ordered, visited); + head.setNumblocks(1); } } @@ -446,23 +448,57 @@ public class Graph { if (stopPart != null) { for (GraphPart p : parts) { for (GraphPart sp : stopPart) { - if (sp == p || p.leadsTo(localData, this, code, sp, loops, throwStates, false)) { + if (sp == p || p.leadsTo(localData, this, code, sp, new ArrayList() /*IGNORE LOOPS*/, throwStates, false)) { partsLeadingToStopPart.add(p); } } } } + if (debugPrintLoopList) { + System.err.println("commonset:"); + for (PartCommon pc : commonSet) { + System.err.println("- " + pc); + } + + System.err.println("parts:"); + for(GraphPart p :parts) { + System.err.println("- "+p); + } + + if (stopPart != null) { + System.err.println("stopparts:"); + for(GraphPart p :stopPart) { + System.err.println("- "+p); + } + } + + System.err.println("partsLeadingToStopPart:"); + for (GraphPart p : partsLeadingToStopPart) { + System.err.println("- " + p); + } + + if (partsLeadingToStopPart.isEmpty()) { + return null; //? + } + } + loopc: for (PartCommon pc : commonSet) { for (GraphPart p : partsLeadingToStopPart) { if (p != pc.part && !p.leadsTo(localData, this, code, pc.part, loops, throwStates, false)) { + if (debugPrintLoopList) { + System.err.println("ignoring " + pc.part + ", " + p + " does not lead to it"); + } continue loopc; } } if (pc.level <= 1) { return null; } + if (debugPrintLoopList) { + System.err.println("selected " + pc.part); + } return pc.part; } @@ -489,6 +525,32 @@ public class Graph { return "" + part.toString() + " (level=" + level; } + @Override + public int hashCode() { + int hash = 5; + hash = 71 * hash + Objects.hashCode(this.part); + hash = 71 * hash + this.level; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PartCommon other = (PartCommon) obj; + if (this.level != other.level) { + return false; + } + return Objects.equals(this.part, other.part); + } + } public GraphPart getNextNoJump(GraphPart part, BaseLocalData localData) { @@ -569,6 +631,11 @@ public class Graph { getLoops(localData, heads.get(0), loops, throwStates, null); afterGetLoops(localData, path, allParts); + + //TODO: Make getPrecontinues faster + getBackEdges(localData, loops, throwStates); + + new GraphPrecontinueDetector().detectPrecontinues(heads, allParts, loops, throwStates); if (debugPrintLoopList) { System.err.println(""); for (Loop el : loops) { @@ -576,12 +643,6 @@ public class Graph { } System.err.println(""); } - - //TODO: Make getPrecontinues faster - getBackEdges(localData, loops, throwStates); - - new GraphPrecontinueDetector().detectPrecontinues(heads, allParts, loops, throwStates); - /*System.err.println(""); for (Loop el : loops) { System.err.println(el); @@ -593,8 +654,19 @@ public class Graph { if (localData.secondPassData == null) { SecondPassData secondPassData = prepareSecondPass(ret); - if (secondPassData != null) { - throw new SecondPassException(secondPassData); + if (secondPassData == null) { + if (!localData.allSwitchParts.isEmpty()) { + secondPassData = new SecondPassData(); + secondPassData.allSwitchParts = localData.allSwitchParts; + throw new SecondPassException(secondPassData); + } + } else { + if (secondPassData.getClass() == SecondPassData.class && localData.allSwitchParts.isEmpty()) { + //nothing + } else { + secondPassData.allSwitchParts = localData.allSwitchParts; + throw new SecondPassException(secondPassData); + } } } @@ -1805,8 +1877,28 @@ public class Graph { Map count = new HashMap<>(); GraphPart winner = null; int winnerCount = 0; + int winnerNumBlock = Integer.MAX_VALUE; + + Set bannedCandidates = new HashSet<>(); + if(localData.secondPassData != null) { + bannedCandidates = localData.secondPassData.allSwitchParts; + } + + if (debugPrintLoopList) { + System.err.println("bannedCandidates:"); + for(GraphPart p:bannedCandidates) { + System.err.println("- "+p); + } + + } + for (GraphPart cand : currentLoop.breakCandidates) { - + if (bannedCandidates.contains(cand)) { + if (debugPrintLoopList) { + System.err.println("cand "+cand+" is banned"); + } + continue; + } if (!count.containsKey(cand)) { count.put(cand, 0); } @@ -2690,7 +2782,18 @@ public class Graph { } } - finalComm.addAll(precoCommands); + boolean isAllNotBlock = true; + for (GraphTargetItem ti: precoCommands) { + if (ti instanceof Block) { + isAllNotBlock = false; + break; + } + } + if (isAllNotBlock) { + finalComm.addAll(precoCommands); + } else { + commands.addAll(precoCommands); + } } if (currentLoop.precontinueCommands != null) { finalComm.addAll(currentLoop.precontinueCommands); @@ -3098,7 +3201,7 @@ public class Graph { } } } - + protected SwitchItem handleSwitch(GraphTargetItem switchedObject, GraphSourceItem switchStartItem, List foundGotos, Map> partCodes, Map partCodePos, Set visited, Set allParts, TranslateStack stack, List stopPart, List stopPartKind, List loops, List throwStates, BaseLocalData localData, int staticOperation, String path, List caseValuesMap, GraphPart defaultPart, List caseBodyParts, Reference nextRef, Reference tiRef) throws InterruptedException { @@ -3236,6 +3339,8 @@ public class Graph { } } + localData.allSwitchParts.add(caseBodies.get(i)); + List stopPart2x = new ArrayList<>(stopPart); List stopPartKind2x = new ArrayList<>(stopPartKind); for (GraphPart b : caseBodies) { 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 71646fb5b..bdf849c91 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/GraphPart.java @@ -73,6 +73,8 @@ public class GraphPart implements Serializable { public int finishedTime; public int order; + + public int numBlocks = Integer.MAX_VALUE; //public List throwParts = new ArrayList<>(); @@ -123,6 +125,16 @@ public class GraphPart implements Serializable { ordered.add(this); return time; } + + public void setNumblocks(int numBlocks) { + this.numBlocks = numBlocks; + numBlocks++; + for (GraphPart next : nextParts) { + if (next.numBlocks > numBlocks) { + next.setNumblocks(numBlocks); + } + } + } private boolean leadsTo(BaseLocalData localData, Graph gr, GraphSource code, GraphPart prev, GraphPart part, HashSet visited, List loops, List throwStates, boolean useThrow) throws InterruptedException { if (Thread.currentThread().isInterrupted()) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java index 7f8892d76..fc84bba3f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Loop.java @@ -19,6 +19,7 @@ package com.jpexs.decompiler.graph; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -64,7 +65,12 @@ public class Loop implements Serializable { for (GraphPart p : backEdges) { edgesAsStr.add(p.toString()); } - return "loop(id:" + id + (loopPreContinue != null ? ",precontinue:" + loopPreContinue : "") + ",continue:" + loopContinue + ", break:" + loopBreak + ", phase:" + phase + ", backedges: " + String.join(",", edgesAsStr) + ")"; + Set bcAsStr = new LinkedHashSet<>(); + for (int i = 0; i < breakCandidates.size(); i++) { + bcAsStr.add(breakCandidates.get(i) + " - level " + breakCandidatesLevels.get(i) +" - numblocks " + breakCandidates.get(i).numBlocks); + } + + return "loop(id:" + id + (loopPreContinue != null ? ",precontinue:" + loopPreContinue : "") + ",continue:" + loopContinue + ", break:" + loopBreak + ", phase:" + phase + ", backedges: " + String.join(",", edgesAsStr) + ", breakCandidates: " + String.join(",", bcAsStr) + ")"; } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java index 47f264ce4..749fa173f 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java @@ -16,10 +16,13 @@ */ package com.jpexs.decompiler.graph; +import java.util.HashSet; +import java.util.Set; + /** * * @author JPEXS */ -public abstract class SecondPassData { - +public class SecondPassData { + public Set allSwitchParts = new HashSet<>(); } diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java index ff0f2e1e1..ab3d56ca8 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicAirDecompileTest.java @@ -605,6 +605,8 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "{\r\n" + "obj = {};\r\n" + "item = {};\r\n" + + "for each(item in obj)\r\n" + + "{\r\n" + "switch(item[\"key\"])\r\n" + "{\r\n" + "case 1:\r\n" @@ -614,6 +616,7 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile + "return item;\r\n" + "}\r\n" + "}\r\n" + + "}\r\n" + "return null;\r\n", false); } @@ -1736,6 +1739,57 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile false); } + @Test + public void testWhileBreak2() { + decompileMethod("classic_air", "testWhileBreak2", "var k:int = 8;\r\n" + + "while(true)\r\n" + + "{\r\n" + + "trace(\"X\");\r\n" + + "if(k == 1)\r\n" + + "{\r\n" + + "trace(\"A\");\r\n" + + "return;\r\n" + + "}\r\n" + + "trace(\"Y\");\r\n" + + "if(k < 10)\r\n" + + "{\r\n" + + "trace(\"k1\");\r\n" + + "if(k == 2)\r\n" + + "{\r\n" + + "trace(\"B\");\r\n" + + "if(k > 1)\r\n" + + "{\r\n" + + "trace(\"B1\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"B2\");\r\n" + + "}\r\n" + + "trace(\"Z\");\r\n" + + "if(k == 3)\r\n" + + "{\r\n" + + "trace(\"C\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"Z2\");\r\n" + + "if(k == 4)\r\n" + + "{\r\n" + + "trace(\"D\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"k2\");\r\n" + + "}\r\n" + + "trace(\"E\");\r\n" + + "if(k == 2)\r\n" + + "{\r\n" + + "trace(\"E1\");\r\n" + + "return;\r\n" + + "}\r\n" + + "trace(\"gg\");\r\n" + + "}\r\n" + + "trace(\"ss\");\r\n", + false); + } + @Test public void testWhileContinue() { decompileMethod("classic_air", "testWhileContinue", "var a:int = 5;\r\n" diff --git a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java index 2a95225d6..715d233b2 100644 --- a/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java +++ b/libsrc/ffdec_lib/test/com/jpexs/decompiler/flash/as3decompile/ActionScript3ClassicDecompileTest.java @@ -594,20 +594,23 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes @Test public void testForEachReturn2() { - decompileMethod("classic", "testForEachReturn2", "var item:* = undefined;\r\n" - + "var obj:* = null;\r\n" - + "var x:* = 5;\r\n" - + "if(x != null)\r\n" + decompileMethod("classic", "testForEachReturn2", "var _loc3_:* = undefined;\r\n" + + "var _loc1_:* = null;\r\n" + + "var _loc2_:* = 5;\r\n" + + "if(_loc2_ != null)\r\n" + "{\r\n" - + "obj = {};\r\n" - + "item = {};\r\n" - + "switch(item[\"key\"])\r\n" + + "_loc1_ = {};\r\n" + + "_loc3_ = {};\r\n" + + "for each(_loc3_ in _loc1_)\r\n" + + "{\r\n" + + "switch(_loc3_[\"key\"])\r\n" + "{\r\n" + "case 1:\r\n" + "case 2:\r\n" + "case 3:\r\n" + "case 4:\r\n" - + "return item;\r\n" + + "return _loc3_;\r\n" + + "}\r\n" + "}\r\n" + "}\r\n" + "return null;\r\n", @@ -1736,6 +1739,57 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes false); } + @Test + public void testWhileBreak2() { + decompileMethod("classic", "testWhileBreak2", "var k:int = 8;\r\n" + + "while(true)\r\n" + + "{\r\n" + + "trace(\"X\");\r\n" + + "if(k == 1)\r\n" + + "{\r\n" + + "trace(\"A\");\r\n" + + "return;\r\n" + + "}\r\n" + + "trace(\"Y\");\r\n" + + "if(k < 10)\r\n" + + "{\r\n" + + "trace(\"k1\");\r\n" + + "if(k == 2)\r\n" + + "{\r\n" + + "trace(\"B\");\r\n" + + "if(k > 1)\r\n" + + "{\r\n" + + "trace(\"B1\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"B2\");\r\n" + + "}\r\n" + + "trace(\"Z\");\r\n" + + "if(k == 3)\r\n" + + "{\r\n" + + "trace(\"C\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"Z2\");\r\n" + + "if(k == 4)\r\n" + + "{\r\n" + + "trace(\"D\");\r\n" + + "break;\r\n" + + "}\r\n" + + "trace(\"k2\");\r\n" + + "}\r\n" + + "trace(\"E\");\r\n" + + "if(k == 2)\r\n" + + "{\r\n" + + "trace(\"E1\");\r\n" + + "return;\r\n" + + "}\r\n" + + "trace(\"gg\");\r\n" + + "}\r\n" + + "trace(\"ss\");\r\n", + false); + } + @Test public void testWhileContinue() { decompileMethod("classic", "testWhileContinue", "var a:* = 5;\r\n" diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf index 665881e69..d9434073b 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.air.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf index 56f4bfb48..110a3d9b0 100644 Binary files a/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf and b/libsrc/ffdec_lib/testdata/as3_new/bin/as3_new.flex.swf differ diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as index acae7b144..3e79dcbf0 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/Main.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/Main.as @@ -110,6 +110,7 @@ package TestVector2; TestWhileAnd; TestWhileBreak; + TestWhileBreak2; TestWhileContinue; TestWhileTry; TestWhileTry2; diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestDoWhile4.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestDoWhile4.as index f88fe7f25..3f8829f1e 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestDoWhile4.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestDoWhile4.as @@ -20,7 +20,7 @@ package tests } trace("gg"); } while (k < 10); - trace("ss"); - } + trace("ss"); + } } } diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForEachReturn2.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForEachReturn2.as index 8a329bb26..050e6533f 100644 --- a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForEachReturn2.as +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestForEachReturn2.as @@ -11,8 +11,8 @@ package tests { obj = {}; var item = {}; - //for each (var item in obj) - //{ + for each (var item in obj) + { switch (item["key"]) { case 1: @@ -21,9 +21,9 @@ package tests case 4: return item; }; - //}; + }; }; - return null; + return null; } } } diff --git a/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestWhileBreak2.as b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestWhileBreak2.as new file mode 100644 index 000000000..8ee6905cc --- /dev/null +++ b/libsrc/ffdec_lib/testdata/as3_new/src/tests/TestWhileBreak2.as @@ -0,0 +1,58 @@ +package tests +{ + + public class TestWhileBreak2 + { + public function run():* + { + + var k:int = 8; + while(true) + { + trace("X"); + if (k == 1) { + trace("A"); + return; + } + trace("Y"); + if (k < 10) + { + trace("k1"); + if (k == 2) + { + trace("B"); + if (k > 1) + { + trace("B1"); + break; + } + trace("B2"); + } + trace("Z"); + + if (k == 3) + { + trace("C"); + break; + } + + trace("Z2"); + + if (k == 4) + { + trace("D"); + break; + } + trace("k2"); + } + trace("E"); + if (k == 2) { + trace("E1"); + return; + } + trace("gg"); + } + trace("ss"); + } + } +}