From fab6defe9c3e1751a10d56d8a24244bd0bc724f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Wed, 18 Feb 2026 19:46:35 +0100 Subject: [PATCH] Fixed: #2636 ActionScript - switches vs loop breaks --- CHANGELOG.md | 1 + .../jpexs/decompiler/flash/BaseLocalData.java | 7 +----- .../decompiler/flash/abc/AVM2LocalData.java | 1 - .../decompiler/flash/abc/avm2/AVM2Code.java | 9 +++---- .../flash/abc/avm2/graph/AVM2GraphSource.java | 2 +- .../instructions/InstructionDefinition.java | 4 +-- .../jpexs/decompiler/flash/action/Action.java | 5 ++-- .../decompiler/flash/action/ActionGraph.java | 3 +-- .../flash/action/ActionGraphSource.java | 2 +- .../flash/action/ActionLocalData.java | 4 +-- .../src/com/jpexs/decompiler/graph/Graph.java | 25 ++----------------- .../decompiler/graph/SecondPassData.java | 9 +------ 12 files changed, 16 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 065aa77e0..e23a58b71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - [#2636] ActionScript - Incorrect always-break detection causing insertion of while(true) - [#2636] ActionScript 3 - Incorrect switch detection - AS3 property resolving for KIND_NAMESPACE (like builtin for Strings, etc.) +- [#2636] ActionScript - switches vs loop breaks ## [25.1.0] - 2026-02-17 ### Added 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 ad1c099ad..b6fbd87d0 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/BaseLocalData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/BaseLocalData.java @@ -34,12 +34,7 @@ public abstract class BaseLocalData { /** * Line start instruction */ - public GraphSourceItem lineStartInstruction; - - /** - * Set of all switch parts - */ - public Set allSwitchParts = new HashSet<>(); + public GraphSourceItem lineStartInstruction; /** * Second pass data 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 a5556cfad..5888256c4 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 @@ -258,7 +258,6 @@ public class AVM2LocalData extends BaseLocalData { * @param localData Another AVM2LocalData */ 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 43caa629f..24a11652c 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 @@ -1834,7 +1834,6 @@ public class AVM2Code implements Cloneable { * @param maxTempIndex Max temp index * @param output Output * @param swfVersion SWF version - * @param switchParts Switch parts * @param callStack Call stack * @param abcIndex ABC indexing * @param setLocalPosToGetLocalPos Set local position to get local position @@ -1863,7 +1862,7 @@ public class AVM2Code implements Cloneable { * @throws ConvertException On convert error * @throws InterruptedException On interrupt */ - public void toSourceOutput(Reference maxTempIndex, List output, int swfVersion, 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, ScopeStack localScopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssignmentIps, LinkedIdentityHashSet bottomStackSetLocals, Set usedDeobfuscations) throws ConvertException, InterruptedException { + public void toSourceOutput(Reference maxTempIndex, List output, int swfVersion, 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, ScopeStack localScopeStack, ABC abc, MethodBody body, int start, int end, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, boolean[] visited, HashMap localRegAssignmentIps, LinkedIdentityHashSet bottomStackSetLocals, Set usedDeobfuscations) throws ConvertException, InterruptedException { boolean debugMode = DEBUG_MODE; if (debugMode) { System.err.println("OPEN SubSource:" + start + "-" + end + " " + code.get(start).toString() + " to " + code.get(end).toString()); @@ -1982,7 +1981,7 @@ public class AVM2Code implements Cloneable { } else */ if ((ins.definition instanceof ReturnValueIns) || (ins.definition instanceof ReturnVoidIns) || (ins.definition instanceof ThrowIns)) { - ins.definition.translate(maxTempIndex, usedDeobfuscations, swfVersion, switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssignmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); + ins.definition.translate(maxTempIndex, usedDeobfuscations, swfVersion, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssignmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); //ip = end + 1; break; } else if (ins.definition instanceof NewFunctionIns) { @@ -2018,13 +2017,13 @@ public class AVM2Code implements Cloneable { } } // What to do when hasDup is false? - ins.definition.translate(maxTempIndex, usedDeobfuscations, swfVersion, switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssignmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); + ins.definition.translate(maxTempIndex, usedDeobfuscations, swfVersion, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssignmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); NewFunctionAVM2Item nft = (NewFunctionAVM2Item) stack.peek(); nft.functionName = functionName; ip++; } else { try { - ins.definition.translate(maxTempIndex, usedDeobfuscations, swfVersion, switchParts, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssignmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); + ins.definition.translate(maxTempIndex, usedDeobfuscations, swfVersion, callStack, abcIndex, setLocalPosToGetLocalPos, lineStartItem, isStatic, scriptIndex, classIndex, localRegs, stack, scopeStack, localScopeStack, ins, output, body, abc, localRegNames, localRegTypes, fullyQualifiedNames, path, localRegAssignmentIps, ip, this, thisHasDefaultToPrimitive, bottomStackSetLocals); if (stack.size() == 1 && (stack.peek() instanceof SetLocalAVM2Item)) { bottomStackSetLocals.add((SetLocalAVM2Item) stack.peek()); 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 3eb146c90..67caa0039 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 @@ -200,7 +200,7 @@ public class AVM2GraphSource extends GraphSource { @Override public void translatePart(List output, Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException { Reference lineStartItem = new Reference<>(localData.lineStartInstruction); - code.toSourceOutput(localData.maxTempIndex, output, localData.swfVersion, localData.allSwitchParts, ((AVM2LocalData) localData).callStack, ((AVM2LocalData) localData).abcIndex, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).thisHasDefaultToPrimitive, lineStartItem, path, part, false, isStatic, scriptIndex, classIndex, localRegs, stack, ((AVM2LocalData) localData).scopeStack, ((AVM2LocalData) localData).localScopeStack, abc, body, start, end, localRegNames, ((AVM2LocalData) localData).localRegTypes, fullyQualifiedNames, new boolean[size()], localRegAssignmentIps, ((AVM2LocalData) localData).bottomSetLocals, localData.usedDeobfuscations); + code.toSourceOutput(localData.maxTempIndex, output, localData.swfVersion, ((AVM2LocalData) localData).callStack, ((AVM2LocalData) localData).abcIndex, ((AVM2LocalData) localData).setLocalPosToGetLocalPos, ((AVM2LocalData) localData).thisHasDefaultToPrimitive, lineStartItem, path, part, false, isStatic, scriptIndex, classIndex, localRegs, stack, ((AVM2LocalData) localData).scopeStack, ((AVM2LocalData) localData).localScopeStack, abc, body, start, end, localRegNames, ((AVM2LocalData) localData).localRegTypes, fullyQualifiedNames, new boolean[size()], localRegAssignmentIps, ((AVM2LocalData) localData).bottomSetLocals, localData.usedDeobfuscations); localData.lineStartInstruction = lineStartItem.getVal(); } 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 dd2052b36..148cfa661 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 @@ -278,7 +278,6 @@ public abstract class InstructionDefinition implements Serializable { * @param maxTempIndex Max temp index * @param usedDeobfuscations Used deobfuscations * @param swfVersion SWF version - * @param switchParts Switch parts * @param callStack Call stack * @param abcIndex ABC indexing * @param setLocalPosToGetLocalPos Set local position to get local position @@ -305,9 +304,8 @@ public abstract class InstructionDefinition implements Serializable { * @param bottomSetLocals Bottom set locals * @throws InterruptedException On interrupt */ - public void translate(Reference maxTempIndex, Set usedDeobfuscations, int swfVersion, Set switchParts, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, Reference lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List output, MethodBody body, ABC abc, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive, LinkedIdentityHashSet bottomSetLocals) throws InterruptedException { + public void translate(Reference maxTempIndex, Set usedDeobfuscations, int swfVersion, List callStack, AbcIndexing abcIndex, Map> setLocalPosToGetLocalPos, Reference lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List output, MethodBody body, ABC abc, HashMap localRegNames, HashMap localRegTypes, List fullyQualifiedNames, String path, HashMap localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive, LinkedIdentityHashSet bottomSetLocals) 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 cf483a8f8..27dfb0d1c 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 @@ -1235,7 +1235,6 @@ public abstract class Action implements GraphSourceItem { * @param usedDeobfuscations Used deobfuscations * @param output Output * @param graph ActionGraph - * @param switchParts Switch parts * @param secondPassData Second pass data * @param insideDoInitAction Inside DoInitAction? * @param lineStartActionRef Line start action reference @@ -1253,11 +1252,11 @@ public abstract class Action implements GraphSourceItem { * @throws InterruptedException On interrupt * @throws GraphPartChangeException On graph part change */ - public static void actionsPartToTree(Set usedDeobfuscations, List output, ActionGraph graph, Set switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference lineStartActionRef, 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 void actionsPartToTree(Set usedDeobfuscations, List output, ActionGraph graph, SecondPassData secondPassData, boolean insideDoInitAction, Reference lineStartActionRef, 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(switchParts, secondPassData, insideDoInitAction, registerNames, variables, functions, graph.getUninitializedClassTraits(), usedDeobfuscations); + ActionLocalData localData = new ActionLocalData(secondPassData, insideDoInitAction, registerNames, variables, functions, graph.getUninitializedClassTraits(), usedDeobfuscations); localData.lineStartAction = lineStartActionRef.getVal(); int ip = start; boolean isWhile = false; 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 cfc6705af..3c149b049 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 @@ -1023,10 +1023,9 @@ public class ActionGraph extends Graph { checkSecondPassSwitches(processedIfs, list, spd.switchParts, spd.switchOnFalseParts, spd.switchCaseExpressions); - if (spd.switchParts.isEmpty() && !localData.gotosUsed.getVal() && localData.allSwitchParts.isEmpty()) { + if (spd.switchParts.isEmpty() && !localData.gotosUsed.getVal()) { return null; //no need to second pass } - spd.allSwitchParts.addAll(localData.allSwitchParts); return spd; } 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 24b367f25..ffad1f635 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 @@ -212,7 +212,7 @@ public class ActionGraphSource extends GraphSource { public void translatePart(List output, Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException { Reference fi = new Reference<>(localData.lineStartInstruction); - Action.actionsPartToTree(localData.usedDeobfuscations, output, (ActionGraph) graph, localData.allSwitchParts, localData.secondPassData, this.insideDoInitAction, fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path, charset); + Action.actionsPartToTree(localData.usedDeobfuscations, output, (ActionGraph) graph, localData.secondPassData, this.insideDoInitAction, fi, registerNames, variables, functions, stack, actions, start, end, version, staticOperation, path, charset); localData.lineStartInstruction = fi.getVal(); } 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 d71aa78a5..3a280aa93 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 @@ -102,7 +102,6 @@ public class ActionLocalData extends BaseLocalData { /** * Constructs new ActionLocalData * - * @param switchParts Switch parts * @param secondPassData Second pass data * @param insideDoInitAction Is inside doInitAction * @param regNames Register names @@ -110,8 +109,7 @@ public class ActionLocalData extends BaseLocalData { * @param functions Functions * @param uninitializedClassTraits Uninitialized class traits */ - public ActionLocalData(Set switchParts, SecondPassData secondPassData, boolean insideDoInitAction, HashMap regNames, HashMap variables, HashMap functions, Map> uninitializedClassTraits, Set usedDeobfuscations) { - this.allSwitchParts = switchParts; + public ActionLocalData(SecondPassData secondPassData, boolean insideDoInitAction, HashMap regNames, HashMap variables, HashMap functions, Map> uninitializedClassTraits, Set usedDeobfuscations) { this.regNames = regNames; this.variables = variables; this.functions = functions; 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 8b34fbeaf..53b78a90d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/Graph.java @@ -1433,11 +1433,10 @@ public class Graph { * @return Second pass data or null */ protected SecondPassData prepareSecondPass(BaseLocalData localData, List list) { - if (localData.allSwitchParts.isEmpty() && !localData.gotosUsed.getVal()) { + if (!localData.gotosUsed.getVal()) { return null; } SecondPassData spd = new SecondPassData(); - spd.allSwitchParts.addAll(localData.allSwitchParts); return spd; } @@ -2998,19 +2997,7 @@ 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); - } - } + int winnerNumBlock = Integer.MAX_VALUE; for (GraphPart cand : currentLoop.breakCandidates) { if (removedX.contains(cand)) { @@ -3019,12 +3006,6 @@ public class Graph { } continue; } - if (bannedCandidates.contains(cand)) { - if (debugPrintLoopList) { - System.err.println("cand " + cand + " is banned"); - } - continue; - } if (!count.containsKey(cand)) { count.put(cand, 0); } @@ -4956,8 +4937,6 @@ 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/SecondPassData.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java index 23f236d30..e635c0733 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/graph/SecondPassData.java @@ -16,18 +16,11 @@ */ package com.jpexs.decompiler.graph; -import java.util.HashSet; -import java.util.Set; - /** * Second pass data. * * @author JPEXS */ public class SecondPassData { - - /** - * All switch parts. - */ - public Set allSwitchParts = new HashSet<>(); + }