Fixed: #2636 ActionScript - switches vs loop breaks

This commit is contained in:
Jindra Petřík
2026-02-18 19:46:35 +01:00
parent 7bc96f67f3
commit fab6defe9c
12 changed files with 16 additions and 56 deletions

View File

@@ -34,12 +34,7 @@ public abstract class BaseLocalData {
/**
* Line start instruction
*/
public GraphSourceItem lineStartInstruction;
/**
* Set of all switch parts
*/
public Set<GraphPart> allSwitchParts = new HashSet<>();
public GraphSourceItem lineStartInstruction;
/**
* Second pass data

View File

@@ -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;

View File

@@ -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<Integer> maxTempIndex, List<GraphTargetItem> output, int swfVersion, Set<GraphPart> switchParts, List<MethodBody> callStack, AbcIndexing abcIndex, Map<Integer, Set<Integer>> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference<GraphSourceItem> lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, ABC abc, MethodBody body, int start, int end, HashMap<Integer, String> localRegNames, HashMap<Integer, GraphTargetItem> localRegTypes, List<DottedChain> fullyQualifiedNames, boolean[] visited, HashMap<Integer, Integer> localRegAssignmentIps, LinkedIdentityHashSet<SetLocalAVM2Item> bottomStackSetLocals, Set<String> usedDeobfuscations) throws ConvertException, InterruptedException {
public void toSourceOutput(Reference<Integer> maxTempIndex, List<GraphTargetItem> output, int swfVersion, List<MethodBody> callStack, AbcIndexing abcIndex, Map<Integer, Set<Integer>> setLocalPosToGetLocalPos, boolean thisHasDefaultToPrimitive, Reference<GraphSourceItem> lineStartItem, String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, ABC abc, MethodBody body, int start, int end, HashMap<Integer, String> localRegNames, HashMap<Integer, GraphTargetItem> localRegTypes, List<DottedChain> fullyQualifiedNames, boolean[] visited, HashMap<Integer, Integer> localRegAssignmentIps, LinkedIdentityHashSet<SetLocalAVM2Item> bottomStackSetLocals, Set<String> 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());

View File

@@ -200,7 +200,7 @@ public class AVM2GraphSource extends GraphSource {
@Override
public void translatePart(List<GraphTargetItem> output, Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException {
Reference<GraphSourceItem> 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();
}

View File

@@ -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<Integer> maxTempIndex, Set<String> usedDeobfuscations, int swfVersion, Set<GraphPart> switchParts, List<MethodBody> callStack, AbcIndexing abcIndex, Map<Integer, Set<Integer>> setLocalPosToGetLocalPos, Reference<GraphSourceItem> lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List<GraphTargetItem> output, MethodBody body, ABC abc, HashMap<Integer, String> localRegNames, HashMap<Integer, GraphTargetItem> localRegTypes, List<DottedChain> fullyQualifiedNames, String path, HashMap<Integer, Integer> localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive, LinkedIdentityHashSet<SetLocalAVM2Item> bottomSetLocals) throws InterruptedException {
public void translate(Reference<Integer> maxTempIndex, Set<String> usedDeobfuscations, int swfVersion, List<MethodBody> callStack, AbcIndexing abcIndex, Map<Integer, Set<Integer>> setLocalPosToGetLocalPos, Reference<GraphSourceItem> lineStartItem, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, ScopeStack localScopeStack, AVM2Instruction ins, List<GraphTargetItem> output, MethodBody body, ABC abc, HashMap<Integer, String> localRegNames, HashMap<Integer, GraphTargetItem> localRegTypes, List<DottedChain> fullyQualifiedNames, String path, HashMap<Integer, Integer> localRegsAssignmentIps, int ip, AVM2Code code, boolean thisHasDefaultToPrimitive, LinkedIdentityHashSet<SetLocalAVM2Item> bottomSetLocals) throws InterruptedException {
AVM2LocalData localData = new AVM2LocalData();
localData.allSwitchParts = switchParts;
localData.isStatic = isStatic;
localData.scriptIndex = scriptIndex;
localData.classIndex = classIndex;

View File

@@ -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<String> usedDeobfuscations, List<GraphTargetItem> output, ActionGraph graph, Set<GraphPart> switchParts, SecondPassData secondPassData, boolean insideDoInitAction, Reference<GraphSourceItem> lineStartActionRef, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, TranslateStack stack, List<Action> actions, int start, int end, int version, int staticOperation, String path, String charset) throws InterruptedException, GraphPartChangeException {
public static void actionsPartToTree(Set<String> usedDeobfuscations, List<GraphTargetItem> output, ActionGraph graph, SecondPassData secondPassData, boolean insideDoInitAction, Reference<GraphSourceItem> lineStartActionRef, HashMap<Integer, String> registerNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, TranslateStack stack, List<Action> 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;

View File

@@ -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;
}

View File

@@ -212,7 +212,7 @@ public class ActionGraphSource extends GraphSource {
public void translatePart(List<GraphTargetItem> output, Graph graph, GraphPart part, BaseLocalData localData, TranslateStack stack, int start, int end, int staticOperation, String path) throws InterruptedException, GraphPartChangeException {
Reference<GraphSourceItem> 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();
}

View File

@@ -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<GraphPart> switchParts, SecondPassData secondPassData, boolean insideDoInitAction, HashMap<Integer, String> regNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, Map<String, Map<String, Trait>> uninitializedClassTraits, Set<String> usedDeobfuscations) {
this.allSwitchParts = switchParts;
public ActionLocalData(SecondPassData secondPassData, boolean insideDoInitAction, HashMap<Integer, String> regNames, HashMap<String, GraphTargetItem> variables, HashMap<String, GraphTargetItem> functions, Map<String, Map<String, Trait>> uninitializedClassTraits, Set<String> usedDeobfuscations) {
this.regNames = regNames;
this.variables = variables;
this.functions = functions;

View File

@@ -1433,11 +1433,10 @@ public class Graph {
* @return Second pass data or null
*/
protected SecondPassData prepareSecondPass(BaseLocalData localData, List<GraphTargetItem> 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<GraphPart, Integer> count = new HashMap<>();
GraphPart winner = null;
int winnerCount = 0;
int winnerNumBlock = Integer.MAX_VALUE;
Set<GraphPart> 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<GraphPart> stopPart2x = new ArrayList<>(stopPart);
List<StopPartKind> stopPartKind2x = new ArrayList<>(stopPartKind);
for (GraphPart b : caseBodies) {

View File

@@ -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<GraphPart> allSwitchParts = new HashSet<>();
}