Fixed: AS3 break loop in catch clause

This commit is contained in:
Jindra Petřík
2021-02-09 18:33:26 +01:00
parent 1b505080de
commit a97f57c40f
27 changed files with 653 additions and 39 deletions

View File

@@ -21,8 +21,10 @@ import com.jpexs.decompiler.flash.FinalProcessLocalData;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.AVM2LocalData;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
import com.jpexs.decompiler.flash.abc.avm2.CodeStats;
import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction;
import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition;
import com.jpexs.decompiler.flash.abc.avm2.instructions.construction.NewCatchIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.debug.DebugLineIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.IfStrictEqIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.jumps.JumpIns;
@@ -32,6 +34,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.DecLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.GetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.IncLocalIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.KillIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.localregs.SetLocalTypeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.HasNext2Ins;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other.LabelIns;
@@ -43,6 +46,7 @@ import com.jpexs.decompiler.flash.abc.avm2.instructions.other2.DecLocalPIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.other2.IncLocalPIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PopIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushByteIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushScopeIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.CoerceAIns;
import com.jpexs.decompiler.flash.abc.avm2.instructions.types.ConvertIIns;
import com.jpexs.decompiler.flash.abc.avm2.model.AVM2Item;
@@ -166,10 +170,15 @@ public class AVM2Graph extends Graph {
}
@Override
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part) {
AVM2LocalData aLocalData = (AVM2LocalData) localData;
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part, List<ThrowState> throwStates) {
/*AVM2LocalData aLocalData = (AVM2LocalData) localData;
if (aLocalData.finallyTargetParts.containsValue(part)) {
return false;
}*/
for (ThrowState ts : throwStates) {
if (ts.targetPart == part) {
return false;
}
}
return true;
}
@@ -177,7 +186,6 @@ public class AVM2Graph extends Graph {
@Override
protected void beforeGetLoops(BaseLocalData localData, String path, Set<GraphPart> allParts, List<ThrowState> throwStates) throws InterruptedException {
AVM2LocalData avm2LocalData = ((AVM2LocalData) localData);
for (int e = 0; e < body.exceptions.length; e++) {
ABCException ex = body.exceptions[e];
if (ex.isFinally()) {
@@ -185,7 +193,6 @@ public class AVM2Graph extends Graph {
}
}
avm2LocalData.codeStats = avm2LocalData.code.getStats(avm2LocalData.abc, avm2LocalData.methodBody, avm2LocalData.methodBody.init_scope_depth, false);
getIgnoredSwitches((AVM2LocalData) localData, allParts);
Set<Integer> integerSwitchesIps = new HashSet<>();
for (GraphPart p : avm2LocalData.ignoredSwitches.values()) {
@@ -2065,7 +2072,11 @@ public class AVM2Graph extends Graph {
}
@Override
protected List<ThrowState> getThrowStates(Set<GraphPart> allParts) {
protected List<ThrowState> getThrowStates(BaseLocalData localData, Set<GraphPart> allParts) {
AVM2LocalData avm2LocalData = (AVM2LocalData) localData;
avm2LocalData.codeStats = avm2LocalData.code.getStats(avm2LocalData.abc, avm2LocalData.methodBody, avm2LocalData.methodBody.init_scope_depth, false);
List<ThrowState> ret = new ArrayList<>();
for (int e = 0; e < body.exceptions.length; e++) {
ThrowState ts = new ThrowState();
@@ -2079,9 +2090,113 @@ public class AVM2Graph extends Graph {
ts.throwingParts.add(p);
}
}
GraphPart part = ts.targetPart;
boolean wasNewCatch = false;
int scopePos = -1;
int ip = part.start;
for (; ip <= part.end; ip++) {
if (avm2code.code.get(ip).definition instanceof NewCatchIns) {
wasNewCatch = true;
}
if (avm2code.code.get(ip).definition instanceof PushScopeIns) {
if (wasNewCatch) {
scopePos = avm2LocalData.codeStats.instructionStats[ip].scopepos_after;
break;
}
}
if (ip == part.end && part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) {
part = part.nextParts.get(0);
ip = part.start - 1;
continue;
}
}
//Search all parts which have same or greater scope level, these all belong to catch
Set<GraphPart> catchParts = new HashSet<>();
if (scopePos > -1) {
walkCatchParts(avm2LocalData.codeStats, part, ip, catchParts, scopePos);
} else {
logger.fine("No newcatch..pushscope found in catch, probably swftools");
part = ts.targetPart;
ip = part.start;
int localRegId = -1;
for (; ip <= part.end; ip++) {
AVM2Instruction ins = avm2code.code.get(ip);
if ((ins.definition instanceof NopIns)
|| (ins.definition instanceof DebugLineIns)
|| (ins.definition instanceof JumpIns)) {
//ignore
} else if (ins.definition instanceof SetLocalTypeIns) {
localRegId = (((SetLocalTypeIns) ins.definition).getRegisterId(ins));
break;
} else {
break;
}
if (ip == part.end && part.nextParts.size() == 1 && part.nextParts.get(0).refs.size() == 1) {
part = part.nextParts.get(0);
ip = part.start - 1;
continue;
}
}
if (localRegId == -1) {
logger.fine("Not even local reg assignment found in catch, weird :-(");
} else {
walkCatchPartsReg(localRegId, part, ip + 1, catchParts, new ArrayList<>(), new HashSet<>());
}
}
ts.catchParts = catchParts;
ret.add(ts);
}
return ret;
}
private void walkCatchPartsReg(int registerId, GraphPart part, int startIp, Set<GraphPart> catchParts, List<GraphPart> path, Set<GraphPart> visited) {
if (visited.contains(part)) {
return;
}
visited.add(part);
List<GraphPart> newPath = new ArrayList<>(path);
newPath.add(part);
for (int ip = startIp; ip <= part.end; ip++) {
AVM2Instruction ins = avm2code.code.get(ip);
if (ins.definition instanceof SetLocalTypeIns) {
int setLocalId = ((SetLocalTypeIns) ins.definition).getRegisterId(ins);
if (setLocalId == registerId) {
return;
}
}
if (ins.definition instanceof KillIns) {
int killId = ins.operands[0];
if (killId == registerId) {
return;
}
}
if (ins.definition instanceof GetLocalTypeIns) {
int getLocalId = ((GetLocalTypeIns) ins.definition).getRegisterId(ins);
if (getLocalId == registerId) {
catchParts.addAll(newPath);
}
}
}
for (GraphPart n : part.nextParts) {
walkCatchPartsReg(registerId, n, n.start, catchParts, newPath, visited);
}
}
private void walkCatchParts(CodeStats stats, GraphPart part, int startIp, Set<GraphPart> catchParts, int scopePos) {
if (catchParts.contains(part)) {
return;
}
catchParts.add(part);
for (int ip = startIp; ip <= part.end; ip++) {
if (stats.instructionStats[ip].scopepos_after < scopePos) {
return;
}
}
for (GraphPart n : part.nextParts) {
walkCatchParts(stats, n, n.start, catchParts, scopePos);
}
}
}

View File

@@ -143,7 +143,7 @@ public class ActionGraph extends Graph {
}
@Override
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part) {
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part, List<ThrowState> throwStates) {
if (part.refs.size() <= 1) {
return true;
}

View File

@@ -481,7 +481,7 @@ public class Graph {
}
protected List<ThrowState> getThrowStates(Set<GraphPart> allParts) {
protected List<ThrowState> getThrowStates(BaseLocalData localData, Set<GraphPart> allParts) {
return new ArrayList<>();
}
@@ -512,7 +512,7 @@ public class Graph {
System.err.println("/parts");
}
TranslateStack stack = new TranslateStack(path);
List<ThrowState> throwStates = getThrowStates(allParts);
List<ThrowState> throwStates = getThrowStates(localData, allParts);
beforeGetLoops(localData, path, allParts, throwStates);
List<Loop> loops = new ArrayList<>();
@@ -1170,7 +1170,7 @@ public class Graph {
clearThrowStates(throwStates);
}
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part) {
protected boolean canBeBreakCandidate(BaseLocalData localData, GraphPart part, List<ThrowState> throwStates) {
return true;
}
@@ -1207,11 +1207,8 @@ public class Graph {
}
checkGetLoopsPart(part);
List<ThrowState> currentThrowStates = new ArrayList<>();
for (ThrowState ts : throwStates) {
if (ts.throwingParts.contains(part) && ts.state != 1) {
currentThrowStates.add(ts);
if (ts.throwingParts.contains(part)) {
ts.state = 1;
}
}
@@ -1232,7 +1229,17 @@ public class Graph {
}
}
if (lastP1 != null && canBeBreakCandidate(localData, part)) {
boolean canBeCandidate = true;
if (lastP1 != null) {
for (ThrowState ts : throwStates) {
if (!ts.catchParts.contains(lastP1.loopContinue) && ts.catchParts.contains(part)) {
canBeCandidate = false;
break;
}
}
}
if (lastP1 != null && canBeCandidate && canBeBreakCandidate(localData, part, throwStates)) {
if (lastP1.breakCandidates.contains(part)) {
lastP1.breakCandidates.add(part);
lastP1.breakCandidatesLevels.add(level);
@@ -1274,6 +1281,15 @@ public class Graph {
//loopContinues.add(part);
}
for (ThrowState ts : throwStates) {
if (ts.throwingParts.contains(part)) {
GraphPart t = ts.targetPart;
if (!visited.contains(t)) {
getLoopsWalk(localData, t, loops, throwStates, stopPart, false, visited, level);
}
}
}
if (part.nextParts.size() == 2 && !partIsSwitch(part)) {
List<GraphPart> nps;/* = new ArrayList<>(part.nextParts);
@@ -1326,22 +1342,13 @@ public class Graph {
getLoopsWalk(localData, part.nextParts.get(0), loops, throwStates, stopPart, false, visited, level);
}
List<Loop> loops2 = new ArrayList<>(loops);
/*List<Loop> loops2 = new ArrayList<>(loops);
for (Loop l : loops2) {
l.breakCandidatesLocked++;
}
for (ThrowState ts : throwStates) {
if (ts.throwingParts.contains(part) && (currentThrowStates.contains(ts) || ts.state != 1)) {
GraphPart t = ts.targetPart;
if (!visited.contains(t)) {
getLoopsWalk(localData, t, loops, throwStates, stopPart, false, visited, level + 1 /*???*/);
}
}
}
for (Loop l : loops2) {
}*/
/*for (Loop l : loops2) {
l.breakCandidatesLocked--;
}
}*/
if (isLoop && currentLoop != null) {
GraphPart found;
@@ -1386,7 +1393,7 @@ public class Graph {
findPartsOutsideTry(ts, cand, outsideTry, new HashSet<>());
for (int j = outsideTry.size() - 1; j >= 0; j--) {
if (!canBeBreakCandidate(localData, outsideTry.get(j))) {
if (!canBeBreakCandidate(localData, outsideTry.get(j), throwStates)) {
outsideTry.remove(j);
}
}
@@ -1463,7 +1470,7 @@ public class Graph {
currentLoop.breakCandidates.add(other);
currentLoop.breakCandidatesLevels.add(curLev);
break loopcand;
}
}
@@ -1486,7 +1493,7 @@ public class Graph {
removed.put(found, maxlevel);
}
} while ((found != null) && (currentLoop.breakCandidates.size() > 1));
Map<GraphPart, Integer> count = new HashMap<>();
GraphPart winner = null;
int winnerCount = 0;
@@ -2082,7 +2089,7 @@ public class Graph {
GraphTargetItem leftSide = expr.getNotCoercedNoDup();
boolean hideEmptyTrueFalse = true;
if (leftSide instanceof DuplicateItem) {
isIf = false;
if (hideEmptyTrueFalse && prevExpr.getNotCoercedNoDup() instanceof FalseItem) {

View File

@@ -13,6 +13,7 @@ public class ThrowState {
public Set<GraphPart> throwingParts = new HashSet<>();
public GraphPart targetPart;
public Set<GraphPart> catchParts = new HashSet<>();
@Override
public int hashCode() {