perf: optimize recursion, avoid stackoverflow on larger scripts (#2672)

This commit is contained in:
Jindra Petřík
2026-03-20 07:56:39 +01:00
parent cd8a9dbdad
commit 2bc1c4e012
24 changed files with 31249 additions and 623 deletions

View File

@@ -322,6 +322,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -981,100 +982,123 @@ public class AVM2Code implements Cloneable {
return Undefined.INSTANCE;
}
private class DebugFileLineWindow {
String debugFile;
int debugLine;
int pos;
Set<Integer> seen;
Set<Integer> seenMethods;
public DebugFileLineWindow(String debugFile, int debugLine, int pos, Set<Integer> seen, Set<Integer> seenMethods) {
this.debugFile = debugFile;
this.debugLine = debugLine;
this.pos = pos;
this.seen = seen;
this.seenMethods = seenMethods;
}
}
/**
* Calculates the line debug file/line info and sets it to the instructions.
*
* @param abc ABC
*/
public void calculateDebugFileLine(ABC abc) {
calculateDebugFileLine(null, 0, 0, abc, new HashSet<>(), new HashSet<>());
}
/**
* Calculates the line debug file/line info and sets it to the instructions.
*
* @param debugFile Debug file
* @param debugLine Debug line
* @param pos Position
* @param abc ABC
* @param seen Seen instructions
* @param seenMethods Seen methods
* @return True of seen.
*/
private boolean calculateDebugFileLine(String debugFile, int debugLine, int pos, ABC abc, Set<Integer> seen, Set<Integer> seenMethods) {
while (pos < code.size()) {
AVM2Instruction ins = code.get(pos);
if (seen.contains(pos)) {
return true;
}
Queue<DebugFileLineWindow> q = new ArrayDeque<>();
q.add(new DebugFileLineWindow(null, 0, 0, new HashSet<>(), new HashSet<>()));
seen.add(pos);
String debugFile;
int debugLine;
int pos;
Set<Integer> seen;
Set<Integer> seenMethods;
if (ins.definition instanceof DebugFileIns) {
debugFile = abc.constants.getString(ins.operands[0]);
}
while (!q.isEmpty()) {
DebugFileLineWindow window = q.poll();
pos = window.pos;
debugFile = window.debugFile;
debugLine = window.debugLine;
seen = window.seen;
seenMethods = window.seenMethods;
while (pos < code.size()) {
AVM2Instruction ins = code.get(pos);
if (seen.contains(pos)) {
break;
}
if (ins.definition instanceof DebugLineIns) {
debugLine = ins.operands[0];
}
seen.add(pos);
ins.setFileLine(debugFile, debugLine);
if (ins.definition instanceof DebugFileIns) {
debugFile = abc.constants.getString(ins.operands[0]);
}
if (ins.definition instanceof NewFunctionIns) {
//Only analyze NewFunction objects that are not immediately discarded by Pop.
//This avoids bogus functions used in obfuscation or special compilers that can lead to infinite recursion.
if ((pos + 1 < code.size()) && !(code.get(pos + 1).definition instanceof PopIns)) {
int newMethodInfo = ins.operands[0];
if (!seenMethods.contains(newMethodInfo)) { //avoid recursion
MethodBody innerBody = abc.findBody(newMethodInfo);
if (innerBody != null) { //Ignore functions without body
seenMethods.add(newMethodInfo);
innerBody.getCode().calculateDebugFileLine(debugFile, debugLine, 0, abc, new HashSet<>(), seenMethods);
if (ins.definition instanceof DebugLineIns) {
debugLine = ins.operands[0];
}
ins.setFileLine(debugFile, debugLine);
if (ins.definition instanceof NewFunctionIns) {
//Only analyze NewFunction objects that are not immediately discarded by Pop.
//This avoids bogus functions used in obfuscation or special compilers that can lead to infinite recursion.
if ((pos + 1 < code.size()) && !(code.get(pos + 1).definition instanceof PopIns)) {
int newMethodInfo = ins.operands[0];
if (!seenMethods.contains(newMethodInfo)) { //avoid recursion
MethodBody innerBody = abc.findBody(newMethodInfo);
if (innerBody != null) { //Ignore functions without body
seenMethods.add(newMethodInfo);
//innerBody.getCode().calculateDebugFileLine(debugFile, debugLine, 0, abc, new HashSet<>(), seenMethods);
q.offer(new DebugFileLineWindow(debugFile, debugLine, 0, new HashSet<Integer>(), seenMethods));
}
}
}
}
}
if (ins.definition instanceof ReturnValueIns) {
return true;
}
if (ins.definition instanceof ReturnVoidIns) {
return true;
}
if (ins.definition instanceof JumpIns) {
try {
pos = adr2pos(ins.getTargetAddress());
continue;
} catch (ConvertException ex) {
return false;
}
} else if (ins.definition instanceof IfTypeIns) {
try {
int newpos = adr2pos(ins.getTargetAddress());
calculateDebugFileLine(debugFile, debugLine, newpos, abc, seen, seenMethods);
} catch (ConvertException ex) {
return false;
}
}
if (ins.definition instanceof LookupSwitchIns) {
for (int i = 0; i < ins.operands.length; i++) {
if (i == 1) {
continue;
}
}
if (ins.definition instanceof ReturnValueIns) {
break;
}
if (ins.definition instanceof ReturnVoidIns) {
break;
}
if (ins.definition instanceof JumpIns) {
try {
int newpos = adr2pos(pos2adr(pos) + ins.operands[i]);
if (!calculateDebugFileLine(debugFile, debugLine, newpos, abc, seen, seenMethods)) {
return false;
}
pos = adr2pos(ins.getTargetAddress());
continue;
} catch (ConvertException ex) {
return false;
break;
}
} else if (ins.definition instanceof IfTypeIns) {
try {
int newpos = adr2pos(ins.getTargetAddress());
//calculateDebugFileLine(debugFile, debugLine, newpos, abc, seen, seenMethods);
q.offer(new DebugFileLineWindow(debugFile, debugLine, newpos, seen, seenMethods));
} catch (ConvertException ex) {
break;
}
}
if (ins.definition instanceof LookupSwitchIns) {
for (int i = 0; i < ins.operands.length; i++) {
if (i == 1) {
continue;
}
try {
int newpos = adr2pos(pos2adr(pos) + ins.operands[i]);
q.offer(new DebugFileLineWindow(debugFile, debugLine, newpos, seen, seenMethods));
/*if (!calculateDebugFileLine(debugFile, debugLine, newpos, abc, seen, seenMethods)) {
return false;
}*/
} catch (ConvertException ex) {
break;
}
}
}
pos++;
}
pos++;
}
return true;
}
/**
@@ -2027,7 +2051,7 @@ public class AVM2Code implements Cloneable {
try {
/*System.err.println("executing ins " + ins);
System.err.println("::::::::::::::::::::::::");
*/
*/
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);
/*System.err.println("output:");
for (GraphTargetItem ti : output) {
@@ -2044,11 +2068,11 @@ public class AVM2Code implements Cloneable {
System.err.println("" + ti.toString(LocalData.create(new ArrayList<MethodBody>(), abcIndex, abc, localRegNames, new ArrayList<DottedChain>(), new HashSet<Integer>(), ScriptExportMode.AS, swfVersion, new HashSet<String>(), classIndex)));
}
System.err.println("/---------------");
*/
*/
if (stack.size() == 1 && (stack.peek() instanceof SetLocalAVM2Item)) {
bottomStackSetLocals.add((SetLocalAVM2Item) stack.peek());
}
} catch (RuntimeException re) {
/*String last="";
int len=5;
@@ -2199,15 +2223,13 @@ public class AVM2Code implements Cloneable {
return assignment;
}
GraphTargetItem vtype = TypeItem.UNBOUNDED;
if (
(assignment instanceof PostIncrementAVM2Item)
if ((assignment instanceof PostIncrementAVM2Item)
|| (assignment instanceof PostDecrementAVM2Item)
|| (assignment instanceof PreIncrementAVM2Item)
|| (assignment instanceof PreIncrementAVM2Item)
) {
|| (assignment instanceof PreIncrementAVM2Item)) {
vtype = assignment.returnType();
} else {
} else {
if (assignment.value instanceof ConvertAVM2Item) {
vtype = ((ConvertAVM2Item) assignment.value).type;
} else if (assignment.value instanceof CoerceAVM2Item) {
@@ -2452,7 +2474,7 @@ public class AVM2Code implements Cloneable {
}
}
}
if ((subItem instanceof PostIncrementAVM2Item)
if ((subItem instanceof PostIncrementAVM2Item)
|| (subItem instanceof PostDecrementAVM2Item)
|| (subItem instanceof PreIncrementAVM2Item)
|| (subItem instanceof PreIncrementAVM2Item)) {
@@ -2475,7 +2497,7 @@ public class AVM2Code implements Cloneable {
setLocal.type = declaredRegisters[setLocal.regIndex].type;
}
}
*/
*/
if (subItem instanceof SetPropertyAVM2Item) {
SetPropertyAVM2Item sp = (SetPropertyAVM2Item) subItem;
if (sp.object instanceof FindPropertyAVM2Item) {
@@ -2990,7 +3012,7 @@ public class AVM2Code implements Cloneable {
uniteLocalsDeclarationTypes(d, list);
return list;
}
private void uniteLocalsDeclarationTypes(DeclarationAVM2Item[] declaredRegs, List<GraphTargetItem> items) {
for (int i = 0; i < items.size(); i++) {
GraphTargetItem currentItem = items.get(i);
@@ -3002,11 +3024,11 @@ public class AVM2Code implements Cloneable {
itemsOnLine.add(item);
}
});
for (GraphTargetItem item : itemsOnLine) {
if (item instanceof SetLocalAVM2Item) {
SetLocalAVM2Item setLocal = (SetLocalAVM2Item) item;
if (declaredRegs[setLocal.regIndex] != null) {
if (declaredRegs[setLocal.regIndex] != null) {
setLocal.type = declaredRegs[setLocal.regIndex].type;
}
}
@@ -3406,6 +3428,19 @@ public class AVM2Code implements Cloneable {
}
}
private class WalkCodeWindow {
int pos;
int stack;
int scope;
public WalkCodeWindow(int pos, int stack, int scope) {
this.pos = pos;
this.stack = stack;
this.scope = scope;
}
}
/**
* Walks code for stats.
*
@@ -3418,103 +3453,119 @@ public class AVM2Code implements Cloneable {
* @return True if success
*/
private boolean walkCode(CodeStats stats, int pos, int stack, int scope, ABC abc, boolean autoFill) {
while (pos < code.size()) {
AVM2Instruction ins = code.get(pos);
if (stats.instructionStats[pos].seen) {
// check stack mismatch here
return true;
}
Queue<WalkCodeWindow> q = new ArrayDeque<>();
q.offer(new WalkCodeWindow(pos, stack, scope));
if (ins.definition instanceof NewFunctionIns) {
MethodBody innerBody = abc.findBody(ins.operands[0]);
if (autoFill) {
innerBody.autoFillStats(abc, stats.initscope + (stats.has_activation ? 1 : 0), false);
loopq:
while (!q.isEmpty()) {
WalkCodeWindow window = q.poll();
pos = window.pos;
stack = window.stack;
scope = window.scope;
while (pos < code.size()) {
AVM2Instruction ins = code.get(pos);
if (stats.instructionStats[pos].seen) {
// check stack mismatch here
//return true
continue loopq;
}
}
stats.instructionStats[pos].seen = true;
stats.instructionStats[pos].stackpos = stack;
stats.instructionStats[pos].scopepos = scope;
int stackDelta = ins.definition.getStackDelta(ins, abc);
int scopeDelta = ins.definition.getScopeStackDelta(ins, abc);
int oldStack = stack;
//+" deltaScope:"+(scopeDelta>0?"+"+scopeDelta:scopeDelta)+" stack:"+stack+" scope:"+scope);
stack += stackDelta;
scope += scopeDelta;
stats.instructionStats[pos].stackpos_after = stack;
stats.instructionStats[pos].scopepos_after = scope;
if (stack > stats.maxstack) {
stats.maxstack = stack;
}
if (scope > stats.maxscope) {
stats.maxscope = scope;
}
//System.out.println("stack "+oldStack+(stackDelta>=0?"+"+stackDelta:stackDelta)+" max:"+stats.maxstack+" "+ins);
if ((ins.definition instanceof DXNSIns) || (ins.definition instanceof DXNSLateIns)) {
stats.has_set_dxns = true;
}
if (ins.definition instanceof NewActivationIns) {
stats.has_activation = true;
}
if (ins.definition instanceof SetLocalTypeIns) {
handleRegister(stats, ((SetLocalTypeIns) ins.definition).getRegisterId(ins));
} else if (ins.definition instanceof GetLocalTypeIns) {
handleRegister(stats, ((GetLocalTypeIns) ins.definition).getRegisterId(ins));
} else {
for (int i = 0; i < ins.definition.operands.length; i++) {
int op = ins.definition.operands[i];
if (op == DAT_LOCAL_REG_INDEX) {
handleRegister(stats, ins.operands[i]);
if (ins.definition instanceof NewFunctionIns) {
MethodBody innerBody = abc.findBody(ins.operands[0]);
if (autoFill) {
innerBody.autoFillStats(abc, stats.initscope + (stats.has_activation ? 1 : 0), false);
}
}
}
if (ins.definition instanceof ReturnValueIns) {
// check stack=1
return true;
}
if (ins.definition instanceof ReturnVoidIns) {
// check stack=0
return true;
}
if (ins.definition instanceof ThrowIns) {
return true;
}
if (ins.definition instanceof JumpIns) {
try {
pos = adr2pos(ins.getTargetAddress());
continue;
} catch (ConvertException ex) {
return false;
stats.instructionStats[pos].seen = true;
stats.instructionStats[pos].stackpos = stack;
stats.instructionStats[pos].scopepos = scope;
int stackDelta = ins.definition.getStackDelta(ins, abc);
int scopeDelta = ins.definition.getScopeStackDelta(ins, abc);
int oldStack = stack;
//+" deltaScope:"+(scopeDelta>0?"+"+scopeDelta:scopeDelta)+" stack:"+stack+" scope:"+scope);
stack += stackDelta;
scope += scopeDelta;
stats.instructionStats[pos].stackpos_after = stack;
stats.instructionStats[pos].scopepos_after = scope;
if (stack > stats.maxstack) {
stats.maxstack = stack;
}
} else if (ins.definition instanceof IfTypeIns) {
try {
int newpos = adr2pos(ins.getTargetAddress());
walkCode(stats, newpos, stack, scope, abc, autoFill);
} catch (ConvertException ex) {
return false;
if (scope > stats.maxscope) {
stats.maxscope = scope;
}
}
if (ins.definition instanceof LookupSwitchIns) {
for (int i = 0; i < ins.operands.length; i++) {
if (i == 1) {
continue;
}
try {
int newpos = adr2pos(pos2adr(pos) + ins.operands[i]);
if (!walkCode(stats, newpos, stack, scope, abc, autoFill)) {
return false;
//System.out.println("stack "+oldStack+(stackDelta>=0?"+"+stackDelta:stackDelta)+" max:"+stats.maxstack+" "+ins);
if ((ins.definition instanceof DXNSIns) || (ins.definition instanceof DXNSLateIns)) {
stats.has_set_dxns = true;
}
if (ins.definition instanceof NewActivationIns) {
stats.has_activation = true;
}
if (ins.definition instanceof SetLocalTypeIns) {
handleRegister(stats, ((SetLocalTypeIns) ins.definition).getRegisterId(ins));
} else if (ins.definition instanceof GetLocalTypeIns) {
handleRegister(stats, ((GetLocalTypeIns) ins.definition).getRegisterId(ins));
} else {
for (int i = 0; i < ins.definition.operands.length; i++) {
int op = ins.definition.operands[i];
if (op == DAT_LOCAL_REG_INDEX) {
handleRegister(stats, ins.operands[i]);
}
}
}
if (ins.definition instanceof ReturnValueIns) {
// check stack=1
//return true;
continue loopq;
}
if (ins.definition instanceof ReturnVoidIns) {
// check stack=0
//return true;
continue loopq;
}
if (ins.definition instanceof ThrowIns) {
//return true;
continue loopq;
}
if (ins.definition instanceof JumpIns) {
try {
pos = adr2pos(ins.getTargetAddress());
continue;
} catch (ConvertException ex) {
return false;
}
} else if (ins.definition instanceof IfTypeIns) {
try {
int newpos = adr2pos(ins.getTargetAddress());
//walkCode(stats, newpos, stack, scope, abc, autoFill);
q.offer(new WalkCodeWindow(newpos, stack, scope));
} catch (ConvertException ex) {
return false;
}
}
if (ins.definition instanceof LookupSwitchIns) {
for (int i = 0; i < ins.operands.length; i++) {
if (i == 1) {
continue;
}
try {
int newpos = adr2pos(pos2adr(pos) + ins.operands[i]);
/*if (!walkCode(stats, newpos, stack, scope, abc, autoFill)) {
return false;
}*/
q.offer(new WalkCodeWindow(newpos, stack, scope));
} catch (ConvertException ex) {
return false;
}
}
}
pos++;
}
pos++;
}
return true;
}

View File

@@ -130,6 +130,7 @@ import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.helpers.Reference;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -142,6 +143,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
@@ -584,6 +586,18 @@ public class AVM2Graph extends Graph {
}
}
private class WalkLocalRegsUsageWindow {
GraphPart part;
int ip;
public WalkLocalRegsUsageWindow(GraphPart part, int ip) {
this.part = part;
this.ip = ip;
}
}
/**
* Walk local registers usage
*
@@ -597,92 +611,105 @@ public class AVM2Graph extends Graph {
* @param searchRegId Search register ID
*/
private void walkLocalRegsUsage(List<ThrowState> throwStates, AVM2LocalData localData, Set<Integer> getLocalPos, GraphPart startPart, GraphPart part, Set<GraphPart> visited, int ip, int searchRegId) {
if (visited.contains(part) && part != startPart) {
return;
}
Queue<WalkLocalRegsUsageWindow> q = new ArrayDeque<>();
q.offer(new WalkLocalRegsUsageWindow(part, ip));
loopq: while (!q.isEmpty()) {
WalkLocalRegsUsageWindow window = q.poll();
part = window.part;
ip = window.ip;
if (visited.contains(part) && part != startPart) {
continue;
}
if (localData.finallyThrowParts.containsValue(part)) {
if (localData.finallyThrowParts.containsValue(part)) {
visited.add(part);
continue;
}
for (int i = ip; i <= part.end; i++) {
AVM2Instruction ins = avm2code.code.get(i);
if (ins.definition instanceof SetLocalTypeIns) {
int regId = ((SetLocalTypeIns) ins.definition).getRegisterId(ins);
if (searchRegId == regId) {
continue loopq;
}
}
if (ins.definition instanceof GetLocalTypeIns) {
int regId = ((GetLocalTypeIns) ins.definition).getRegisterId(ins);
if (regId == searchRegId) {
getLocalPos.add(i);
}
}
if ((ins.definition instanceof IncLocalIns)
|| (ins.definition instanceof IncLocalIIns)
|| (ins.definition instanceof IncLocalPIns)
|| (ins.definition instanceof DecLocalIns)
|| (ins.definition instanceof DecLocalIIns)
|| (ins.definition instanceof DecLocalPIns)) {
int regId = ins.operands[0];
if (regId == searchRegId) {
getLocalPos.add(i);
}
}
if ((ins.definition instanceof IncLocalPIns)
|| (ins.definition instanceof DecLocalPIns)) {
int regId = ins.operands[1];
if (regId == searchRegId) {
getLocalPos.add(i);
}
}
if (ins.definition instanceof HasNext2Ins) {
int regId1 = ins.operands[0];
if (regId1 == searchRegId) {
getLocalPos.add(i);
}
int regId2 = ins.operands[1];
if (regId2 == searchRegId) {
getLocalPos.add(i);
}
}
}
if (visited.contains(part)) {
continue;
}
visited.add(part);
return;
}
for (int i = ip; i <= part.end; i++) {
AVM2Instruction ins = avm2code.code.get(i);
if (ins.definition instanceof SetLocalTypeIns) {
int regId = ((SetLocalTypeIns) ins.definition).getRegisterId(ins);
if (searchRegId == regId) {
try {
//stop on switch
if (localData.ignoredSwitches.values().contains(part)) {
return;
}
}
if (ins.definition instanceof GetLocalTypeIns) {
int regId = ((GetLocalTypeIns) ins.definition).getRegisterId(ins);
if (regId == searchRegId) {
getLocalPos.add(i);
if (localData.finallyJumps.containsKey(part)) {
GraphPart targetPart = localData.finallyJumps.get(part);
if (localData.defaultParts.containsValue(targetPart)) {
//okay, proceed to finally block
} else if (targetPart.nextParts.size() == 1) {
//continue or break, definitely not a return, there won't be a register usage
//walkLocalRegsUsage(throwStates, localData, getLocalPos, startPart, targetPart.nextParts.get(0), visited, ip, searchRegId);
q.offer(new WalkLocalRegsUsageWindow(targetPart.nextParts.get(0), ip));
continue;
} else {
continue;
}
}
}
if ((ins.definition instanceof IncLocalIns)
|| (ins.definition instanceof IncLocalIIns)
|| (ins.definition instanceof IncLocalPIns)
|| (ins.definition instanceof DecLocalIns)
|| (ins.definition instanceof DecLocalIIns)
|| (ins.definition instanceof DecLocalPIns)) {
int regId = ins.operands[0];
if (regId == searchRegId) {
getLocalPos.add(i);
for (GraphPart p : part.nextParts) {
//walkLocalRegsUsage(throwStates, localData, getLocalPos, startPart, p, visited, p.start, searchRegId);
q.offer(new WalkLocalRegsUsageWindow(p, p.start));
}
}
if ((ins.definition instanceof IncLocalPIns)
|| (ins.definition instanceof DecLocalPIns)) {
int regId = ins.operands[1];
if (regId == searchRegId) {
getLocalPos.add(i);
} finally {
for (ThrowState ts : throwStates) {
if (ts.throwingParts.contains(part)) {
GraphPart p = ts.targetPart;
//walkLocalRegsUsage(throwStates, localData, getLocalPos, startPart, p, visited, p.start, searchRegId);
q.offer(new WalkLocalRegsUsageWindow(p, p.start));
}
}
}
if (ins.definition instanceof HasNext2Ins) {
int regId1 = ins.operands[0];
if (regId1 == searchRegId) {
getLocalPos.add(i);
}
int regId2 = ins.operands[1];
if (regId2 == searchRegId) {
getLocalPos.add(i);
}
}
}
if (visited.contains(part)) {
return;
}
visited.add(part);
try {
//stop on switch
if (localData.ignoredSwitches.values().contains(part)) {
return;
}
if (localData.finallyJumps.containsKey(part)) {
GraphPart targetPart = localData.finallyJumps.get(part);
if (localData.defaultParts.containsValue(targetPart)) {
//okay, proceed to finally block
} else if (targetPart.nextParts.size() == 1) {
//continue or break, definitely not a return, there won't be a register usage
walkLocalRegsUsage(throwStates, localData, getLocalPos, startPart, targetPart.nextParts.get(0), visited, ip, searchRegId);
return;
} else {
return;
}
}
for (GraphPart p : part.nextParts) {
walkLocalRegsUsage(throwStates, localData, getLocalPos, startPart, p, visited, p.start, searchRegId);
}
} finally {
for (ThrowState ts : throwStates) {
if (ts.throwingParts.contains(part)) {
GraphPart p = ts.targetPart;
walkLocalRegsUsage(throwStates, localData, getLocalPos, startPart, p, visited, p.start, searchRegId);
}
}
}
}
@@ -3467,12 +3494,8 @@ public class AVM2Graph extends Graph {
part.nextParts.add(secondPart);
secondPart.refs.add(part);
secondPart.discoveredTime = part.discoveredTime;
secondPart.closedTime = part.closedTime;
secondPart.finishedTime = part.finishedTime;
secondPart.level = part.level;
secondPart.numBlocks = part.numBlocks;
secondPart.order = part.order;
secondPart.path = part.path;
allParts.add(secondPart);
}

View File

@@ -75,6 +75,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
@@ -185,14 +186,7 @@ public class Graph {
if (heads != null) {
return;
}
heads = makeGraph(code, new ArrayList<>(), exceptions);
int time = 1;
List<GraphPart> ordered = new ArrayList<>();
List<GraphPart> visited = new ArrayList<>();
for (GraphPart head : heads) {
time = head.setTime(time, ordered, visited);
head.setNumblocks(1);
}
heads = makeGraph(code, new ArrayList<>(), exceptions);
}
/**
@@ -3305,6 +3299,19 @@ public class Graph {
}
List<GraphTargetItem> originalRet = ret;
Stack<LoopLocalData> loopStack = new Stack<>();
GraphPart nextOnePart = null;
boolean isLoop = false;
Loop currentLoop = null;
List<GraphTargetItem> currentRet = null;
UniversalLoopItem loopItem = null;
TranslateStack sPreLoop = null;
LoopItem li = null;
boolean loopTypeFound = false;
boolean doWhileCandidate = false;
loopPrintGraph:
while (true) {
if (CancellableWorker.isInterrupted()) {
@@ -3333,8 +3340,6 @@ public class Graph {
}
//List<GraphPart> loopContinues = getLoopsContinues(loops);
boolean isLoop = false;
Loop currentLoop = null;
List<GraphTargetItem> precontinueCommands = new ArrayList<>();
boolean vCanHandleLoop = canHandleLoop(localData, part, loops, throwStates);
@@ -3399,7 +3404,7 @@ public class Graph {
}
ret.add(br);
return originalRet;
break loopPrintGraph;
}
if (el.loopPreContinue == part) {
if (currentLoop != null) {
@@ -3410,7 +3415,7 @@ public class Graph {
}
makeAllCommands(ret, stack);
ret.add(new ContinueItem(dialect, null, localData.lineStartInstruction, el.id));
return originalRet;
break loopPrintGraph;
}
if (el.loopContinue == part) {
if (currentLoop != null) {
@@ -3421,7 +3426,7 @@ public class Graph {
}
makeAllCommands(ret, stack);
ret.add(new ContinueItem(dialect, null, localData.lineStartInstruction, el.id));
return originalRet;
break loopPrintGraph;
}
}
@@ -3468,7 +3473,7 @@ public class Graph {
if (debugPrintGraph) {
System.err.println("Stopped on part " + part);
}
return originalRet;
break loopPrintGraph;
}
}
@@ -3477,7 +3482,7 @@ public class Graph {
stack.setConnectedOutput(0, ret, localData);
stack.addToOutput(new ScriptEndItem(dialect));
}
return originalRet;
break loopPrintGraph;
}
boolean vCanHandleVisited = canHandleVisited(localData, part);
@@ -3507,19 +3512,15 @@ public class Graph {
}
ret.add(new GotoItem(dialect, null, localData.lineStartInstruction, labelName));
localData.gotosUsed.setVal(true);
return originalRet;
break loopPrintGraph;
} else {
visited.add(part);
partCodes.put(part, ret);
partCodePos.put(part, ret.size());
}
}
List<GraphTargetItem> currentRet = ret;
UniversalLoopItem loopItem = null;
TranslateStack sPreLoop = stack;
LoopItem li = null;
boolean loopTypeFound = false;
boolean doWhileCandidate = false;
currentRet = ret;
sPreLoop = stack;
if (isLoop) {
//makeAllCommands(currentRet, stack);
stack = (TranslateStack) stack.clone();
@@ -3660,6 +3661,7 @@ public class Graph {
currentRet.addAll(output);
}
}
//********************************END PART DECOMPILING
if (parseNext) {
@@ -3856,13 +3858,14 @@ public class Graph {
if (tiRef.getVal() != null) {
ret.add(tiRef.getVal());
} else {
printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1);
nextOnePart = next;
//printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1);
}
}
pos++;
} //else
GraphPart nextOnePart = null;
if (getNextParts(localData, part).size() == 2 && !partIsSwitch(part)) {
if (nextOnePart == null && getNextParts(localData, part).size() == 2 && !partIsSwitch(part)) {
GraphTargetItem expr = getIfExpression(localData, stack, currentRet);
if (nextOnePart == null) {
@@ -3948,7 +3951,8 @@ public class Graph {
if (currentLoop != null) {
//System.err.println("handling parent loop");
handleLoop(hasEmptyStackPops, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, foundGotos, partCodes, partCodePos, visited, localData, allParts, null /*??*/, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel, sPreLoop);
//handleLoop(hasEmptyStackPops, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, foundGotos, partCodes, partCodePos, visited, localData, allParts, null /*??*/, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel, sPreLoop);
handleLoop(hasEmptyStackPops, new LoopLocalData(null, isLoop, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, stopPart, stopPartKind, ret, sPreLoop), foundGotos, partCodes, partCodePos, visited, localData, allParts, loops, throwStates, staticOperation, path, recursionLevel);
currentLoop.phase = 1;
}
loopItem = newLoopItem;
@@ -4162,12 +4166,12 @@ public class Graph {
}
//currentRet.addAll(out2);
if (next != null) {
printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1);
nextOnePart = next; //printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, next, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1);
//currentRet.addAll();
}
}
} //else
if (getNextParts(localData, part).size() == 1) {
if (nextOnePart == null && getNextParts(localData, part).size() == 1) {
nextOnePart = getNextParts(localData, part).get(0);
}
@@ -4176,40 +4180,106 @@ public class Graph {
}
if (nextOnePart != null) {
printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, getNextParts(localData, part).get(0), stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1);
//printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, stack, allParts, part, nextOnePart, stopPart, stopPartKind, loops, throwStates, currentRet, staticOperation, path, recursionLevel + 1);
//nextOnePart = null;
}
}
if (isLoop && loopItem != null && currentLoop != null) {
handleLoop(hasEmptyStackPops, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, foundGotos, partCodes, partCodePos, visited, localData, allParts, part, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel, sPreLoop);
if (nextOnePart != null) {
if (isLoop) {
loopStack.push(new LoopLocalData(part, isLoop, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, stopPart, stopPartKind, ret, sPreLoop));
}
parent = part;
part = nextOnePart;
nextOnePart = null;
isLoop = false;
li = null;
currentLoop = null;
loopTypeFound = false;
doWhileCandidate = false;
precontinueCommands = null;
ret = currentRet;
sPreLoop = null;
continue;
}
if (isLoop) {
loopStack.push(new LoopLocalData(part, isLoop, loopItem, li, currentLoop, loopTypeFound, doWhileCandidate, precontinueCommands, stopPart, stopPartKind, ret, sPreLoop));
}
break;
}
while (!loopStack.isEmpty()) {
LoopLocalData loopLocalData = loopStack.pop();
if (loopLocalData.isLoop && loopLocalData.loopItem != null && loopLocalData.currentLoop != null) {
handleLoop(hasEmptyStackPops, loopLocalData, foundGotos, partCodes, partCodePos, visited, localData, allParts, loops, throwStates, staticOperation, path, recursionLevel);
}
}
return originalRet;
}
private void handleLoop(Reference<Boolean> hasEmptyStackPops, UniversalLoopItem loopItem, LoopItem li, Loop currentLoop, boolean loopTypeFound, boolean doWhileCandidate, List<GraphTargetItem> precontinueCommands,
List<GotoItem> foundGotos, Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos, Set<GraphPart> visited, BaseLocalData localData, Set<GraphPart> allParts, GraphPart part, List<GraphPart> stopPart, List<StopPartKind> stopPartKind, List<Loop> loops, List<ThrowState> throwStates, List<GraphTargetItem> ret, int staticOperation, String path, int recursionLevel,
TranslateStack sPreLoop
) throws InterruptedException {
processIfs(loopItem.commands);
processSwitches(loopItem.commands, currentLoop.id);
processOther(loopItem.commands, currentLoop.id);
private class LoopLocalData {
checkContinueAtTheEnd(loopItem.commands, currentLoop);
UniversalLoopItem loopItem;
boolean isLoop;
LoopItem li;
Loop currentLoop;
boolean loopTypeFound;
boolean doWhileCandidate;
List<GraphTargetItem> precontinueCommands;
GraphPart part;
List<GraphPart> stopPart;
List<StopPartKind> stopPartKind;
List<GraphTargetItem> ret;
TranslateStack sPreLoop;
public LoopLocalData(GraphPart part, boolean isLoop, UniversalLoopItem loopItem, LoopItem li, Loop currentLoop, boolean loopTypeFound, boolean doWhileCandidate, List<GraphTargetItem> precontinueCommands, List<GraphPart> stopPart, List<StopPartKind> stopPartKind, List<GraphTargetItem> ret, TranslateStack sPreLoop) {
this.isLoop = isLoop;
this.loopItem = loopItem;
this.li = li;
this.currentLoop = currentLoop;
this.loopTypeFound = loopTypeFound;
this.doWhileCandidate = doWhileCandidate;
this.precontinueCommands = precontinueCommands;
this.part = part;
this.stopPart = stopPart;
this.stopPartKind = stopPartKind;
this.ret = ret;
this.sPreLoop = sPreLoop;
}
}
private void handleLoop(Reference<Boolean> hasEmptyStackPops,
LoopLocalData loopLocalData,
List<GotoItem> foundGotos,
Map<GraphPart, List<GraphTargetItem>> partCodes,
Map<GraphPart, Integer> partCodePos,
Set<GraphPart> visited,
BaseLocalData localData,
Set<GraphPart> allParts,
List<Loop> loops, List<ThrowState> throwStates,
int staticOperation, String path, int recursionLevel
) throws InterruptedException {
processIfs(loopLocalData.loopItem.commands);
processSwitches(loopLocalData.loopItem.commands, loopLocalData.currentLoop.id);
processOther(loopLocalData.loopItem.commands, loopLocalData.currentLoop.id);
checkContinueAtTheEnd(loopLocalData.loopItem.commands, loopLocalData.currentLoop);
//DoWhile based on precontinue
if (!loopTypeFound && (!loopItem.commands.isEmpty())) {
if (!loopLocalData.loopTypeFound && (!loopLocalData.loopItem.commands.isEmpty())) {
List<List<GraphTargetItem>> continueCommands1 = new ArrayList<>();
getContinuesCommands(loopItem.commands, continueCommands1, currentLoop.id);
if (!continueCommands1.isEmpty() && doWhileCandidate) {
int index = ret.indexOf(loopItem);
ret.remove(index);
IfItem ifi = (IfItem) precontinueCommands.remove(precontinueCommands.size() - 1);
List<GraphTargetItem> exprList = new ArrayList<>(precontinueCommands);
getContinuesCommands(loopLocalData.loopItem.commands, continueCommands1, loopLocalData.currentLoop.id);
if (!continueCommands1.isEmpty() && loopLocalData.doWhileCandidate) {
int index = loopLocalData.ret.indexOf(loopLocalData.loopItem);
loopLocalData.ret.remove(index);
IfItem ifi = (IfItem) loopLocalData.precontinueCommands.remove(loopLocalData.precontinueCommands.size() - 1);
List<GraphTargetItem> exprList = new ArrayList<>(loopLocalData.precontinueCommands);
boolean invert = false;
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem) && (((BreakItem) ifi.onTrue.get(0)).loopId == currentLoop.id))
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onFalse.get(0)).loopId == currentLoop.id))) {
if (((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem) && (((BreakItem) ifi.onTrue.get(0)).loopId == loopLocalData.currentLoop.id))
&& ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof ContinueItem) && (((ContinueItem) ifi.onFalse.get(0)).loopId == loopLocalData.currentLoop.id))) {
invert = true;
}
@@ -4218,15 +4288,15 @@ public class Graph {
expr = expr.invert(null);
}
exprList.add(expr);
ret.add(index, li = new DoWhileItem(dialect, null, expr.getLineStartItem(), currentLoop, loopItem.commands, exprList));
loopTypeFound = true;
loopLocalData.ret.add(index, loopLocalData.li = new DoWhileItem(dialect, null, expr.getLineStartItem(), loopLocalData.currentLoop, loopLocalData.loopItem.commands, exprList));
loopLocalData.loopTypeFound = true;
}
}
//Loop with condition at the beginning (While)
if (!loopTypeFound && (!loopItem.commands.isEmpty())) {
if (loopItem.commands.get(0) instanceof IfItem) {
IfItem ifi = (IfItem) loopItem.commands.get(0);
if (!loopLocalData.loopTypeFound && (!loopLocalData.loopItem.commands.isEmpty())) {
if (loopLocalData.loopItem.commands.get(0) instanceof IfItem) {
IfItem ifi = (IfItem) loopLocalData.loopItem.commands.get(0);
List<GraphTargetItem> bodyBranch = null;
boolean inverted = false;
@@ -4235,32 +4305,32 @@ public class Graph {
ContinueItem addContinueItem = null;
if ((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem)) {
BreakItem bi = (BreakItem) ifi.onTrue.get(0);
if (bi.loopId == currentLoop.id) {
if (bi.loopId == loopLocalData.currentLoop.id) {
bodyBranch = ifi.onFalse;
inverted = true;
}
} else if ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem)) {
BreakItem bi = (BreakItem) ifi.onFalse.get(0);
if (bi.loopId == currentLoop.id) {
if (bi.loopId == loopLocalData.currentLoop.id) {
bodyBranch = ifi.onTrue;
}
} else if (loopItem.commands.size() == 2
&& (loopItem.commands.get(1) instanceof BreakItem)
} else if (loopLocalData.loopItem.commands.size() == 2
&& (loopLocalData.loopItem.commands.get(1) instanceof BreakItem)
&& ifi.onFalse.isEmpty()
&& !ifi.onTrue.isEmpty()) {
BreakItem bi = (BreakItem) loopItem.commands.get(1);
BreakItem bi = (BreakItem) loopLocalData.loopItem.commands.get(1);
if (ifi.onTrue.isEmpty()) {
inverted = true;
}
bodyBranch = inverted ? ifi.onFalse : ifi.onTrue;
breakpos2 = true;
if (bi.loopId != currentLoop.id) { //it's break of another parent loop
if (bi.loopId != loopLocalData.currentLoop.id) { //it's break of another parent loop
addBreakItem = bi; //we must add it after the loop
} else {
if (!(bodyBranch.get(bodyBranch.size() - 1) instanceof ContinueItem)
&& !(bodyBranch.get(bodyBranch.size() - 1) instanceof BreakItem)
&& !(bodyBranch.get(bodyBranch.size() - 1) instanceof ExitItem)) {
bodyBranch.add(loopItem.commands.get(1));
bodyBranch.add(loopLocalData.loopItem.commands.get(1));
}
}
}
@@ -4286,8 +4356,8 @@ public class Graph {
breakpos2 = true;
}*/
if (bodyBranch != null) { //FIXME
int index = ret.indexOf(loopItem);
ret.remove(index);
int index = loopLocalData.ret.indexOf(loopLocalData.loopItem);
loopLocalData.ret.remove(index);
List<GraphTargetItem> exprList = new ArrayList<>();
GraphTargetItem expr = ifi.expression;
if (inverted) {
@@ -4300,122 +4370,122 @@ public class Graph {
exprList.add(expr);
List<GraphTargetItem> commands = new ArrayList<>();
commands.addAll(bodyBranch);
loopItem.commands.remove(0);
loopLocalData.loopItem.commands.remove(0);
if (breakpos2) {
loopItem.commands.remove(0); //remove that break too
loopLocalData.loopItem.commands.remove(0); //remove that break too
}
commands.addAll(loopItem.commands);
checkContinueAtTheEnd(commands, currentLoop);
commands.addAll(loopLocalData.loopItem.commands);
checkContinueAtTheEnd(commands, loopLocalData.currentLoop);
List<GraphTargetItem> finalComm = new ArrayList<>();
//findGotoTargets - comment this out:
if (!precontinueCommands.isEmpty()) {
if (!loopLocalData.precontinueCommands.isEmpty()) {
List<List<GraphTargetItem>> continueCommands = new ArrayList<>();
getContinuesCommands(commands, continueCommands, currentLoop.id);
getContinuesCommands(commands, continueCommands, loopLocalData.currentLoop.id);
if (continueCommands.isEmpty()) {
commands.addAll(precontinueCommands);
precontinueCommands = new ArrayList<>();
commands.addAll(loopLocalData.precontinueCommands);
loopLocalData.precontinueCommands = new ArrayList<>();
//Single continue and there is break/continue/return/throw at end of the commands
} else if (!commands.isEmpty() && continueCommands.size() == 1) {
GraphTargetItem lastItem = commands.get(commands.size() - 1);
if ((lastItem instanceof BreakItem) || (lastItem instanceof ContinueItem) || (lastItem instanceof ExitItem)) {
continueCommands.get(0).addAll(continueCommands.get(0).size() - 1, precontinueCommands);
precontinueCommands = new ArrayList<>();
continueCommands.get(0).addAll(continueCommands.get(0).size() - 1, loopLocalData.precontinueCommands);
loopLocalData.precontinueCommands = new ArrayList<>();
}
}
finalComm.addAll(precontinueCommands);
finalComm.addAll(loopLocalData.precontinueCommands);
}
if (!finalComm.isEmpty()) {
ret.add(index, li = new ForItem(dialect, expr.getSrc(), expr.getLineStartItem(), currentLoop, new ArrayList<>(), exprList.get(exprList.size() - 1), finalComm, commands));
loopLocalData.ret.add(index, loopLocalData.li = new ForItem(dialect, expr.getSrc(), expr.getLineStartItem(), loopLocalData.currentLoop, new ArrayList<>(), exprList.get(exprList.size() - 1), finalComm, commands));
} else {
ret.add(index, li = new WhileItem(dialect, expr.getSrc(), expr.getLineStartItem(), currentLoop, exprList, commands));
loopLocalData.ret.add(index, loopLocalData.li = new WhileItem(dialect, expr.getSrc(), expr.getLineStartItem(), loopLocalData.currentLoop, exprList, commands));
}
if (addBreakItem != null) {
ret.add(index + 1, addBreakItem);
loopLocalData.ret.add(index + 1, addBreakItem);
}
if (addContinueItem != null) {
ret.add(index + 1, addContinueItem);
loopLocalData.ret.add(index + 1, addContinueItem);
}
loopTypeFound = true;
loopLocalData.loopTypeFound = true;
}
}
}
if (!loopTypeFound && !precontinueCommands.isEmpty()) {
loopItem.commands.addAll(precontinueCommands);
if (!loopLocalData.loopTypeFound && !loopLocalData.precontinueCommands.isEmpty()) {
loopLocalData.loopItem.commands.addAll(loopLocalData.precontinueCommands);
}
//Loop with condition at the end (Do..While)
if (!loopTypeFound && (!loopItem.commands.isEmpty())) {
if (loopItem.commands.get(loopItem.commands.size() - 1) instanceof IfItem) {
IfItem ifi = (IfItem) loopItem.commands.get(loopItem.commands.size() - 1);
if (!loopLocalData.loopTypeFound && (!loopLocalData.loopItem.commands.isEmpty())) {
if (loopLocalData.loopItem.commands.get(loopLocalData.loopItem.commands.size() - 1) instanceof IfItem) {
IfItem ifi = (IfItem) loopLocalData.loopItem.commands.get(loopLocalData.loopItem.commands.size() - 1);
List<GraphTargetItem> bodyBranch = null;
boolean inverted = false;
if ((ifi.onTrue.size() == 1) && (ifi.onTrue.get(0) instanceof BreakItem)) {
BreakItem bi = (BreakItem) ifi.onTrue.get(0);
if (bi.loopId == currentLoop.id) {
if (bi.loopId == loopLocalData.currentLoop.id) {
bodyBranch = ifi.onFalse;
inverted = true;
}
} else if ((ifi.onFalse.size() == 1) && (ifi.onFalse.get(0) instanceof BreakItem)) {
BreakItem bi = (BreakItem) ifi.onFalse.get(0);
if (bi.loopId == currentLoop.id) {
if (bi.loopId == loopLocalData.currentLoop.id) {
bodyBranch = ifi.onTrue;
}
}
if (bodyBranch != null) {
//Condition at the beginning
int index = ret.indexOf(loopItem);
int index = loopLocalData.ret.indexOf(loopLocalData.loopItem);
if (index > -1) {
ret.remove(index);
loopLocalData.ret.remove(index);
List<GraphTargetItem> exprList = new ArrayList<>();
GraphTargetItem expr = ifi.expression;
if (inverted) {
expr = expr.invert(null);
}
checkContinueAtTheEnd(bodyBranch, currentLoop);
checkContinueAtTheEnd(bodyBranch, loopLocalData.currentLoop);
List<GraphTargetItem> commands = new ArrayList<>();
if (!bodyBranch.isEmpty()) {
ret.add(index, loopItem);
loopLocalData.ret.add(index, loopLocalData.loopItem);
} else {
loopItem.commands.remove(loopItem.commands.size() - 1);
commands.addAll(loopItem.commands);
loopLocalData.loopItem.commands.remove(loopLocalData.loopItem.commands.size() - 1);
commands.addAll(loopLocalData.loopItem.commands);
commands.addAll(bodyBranch);
exprList.add(expr);
checkContinueAtTheEnd(commands, currentLoop);
ret.add(index, li = new DoWhileItem(dialect, null, exprList.get(0).getLineStartItem(), currentLoop, commands, exprList));
checkContinueAtTheEnd(commands, loopLocalData.currentLoop);
loopLocalData.ret.add(index, loopLocalData.li = new DoWhileItem(dialect, null, exprList.get(0).getLineStartItem(), loopLocalData.currentLoop, commands, exprList));
}
loopTypeFound = true;
loopLocalData.loopTypeFound = true;
}
}
}
}
if (!loopTypeFound) {
checkContinueAtTheEnd(loopItem.commands, currentLoop);
if (!loopLocalData.loopTypeFound) {
checkContinueAtTheEnd(loopLocalData.loopItem.commands, loopLocalData.currentLoop);
}
currentLoop.phase = 2;
loopLocalData.currentLoop.phase = 2;
GraphTargetItem replaced = checkLoop(ret, li, localData, loops, throwStates, sPreLoop);
if (replaced != li) {
int index = ret.indexOf(li);
ret.remove(index);
GraphTargetItem replaced = checkLoop(loopLocalData.ret, loopLocalData.li, localData, loops, throwStates, loopLocalData.sPreLoop);
if (replaced != loopLocalData.li) {
int index = loopLocalData.ret.indexOf(loopLocalData.li);
loopLocalData.ret.remove(index);
if (replaced != null) {
ret.add(index, replaced);
loopLocalData.ret.add(index, replaced);
}
}
if (currentLoop.loopBreak != null) {
printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, stopPartKind, loops, throwStates, ret, staticOperation, path, recursionLevel + 1);
if (loopLocalData.currentLoop.loopBreak != null) {
printGraph(hasEmptyStackPops, foundGotos, partCodes, partCodePos, visited, localData, loopLocalData.sPreLoop, allParts, loopLocalData.part, loopLocalData.currentLoop.loopBreak, loopLocalData.stopPart, loopLocalData.stopPartKind, loops, throwStates, loopLocalData.ret, staticOperation, path, recursionLevel + 1);
}
}
@@ -4538,160 +4608,183 @@ public class Graph {
alternateEntries.add(ex.target);
}
HashMap<Integer, List<Integer>> refs = code.visitCode(alternateEntries);
List<GraphPart> ret = new ArrayList<>();
List<GraphPart> gret = new ArrayList<>();
boolean[] visited = new boolean[code.size()];
ret.add(makeGraph(null, new GraphPath(), code, startIp, 0, allBlocks, refs, visited));
Queue<MakeGraphWindow> q = new ArrayDeque<>();
//ret.add(makeGraph(null, new GraphPath(), code, startIp, 0, allBlocks, refs, visited));
q.offer(new MakeGraphWindow(null, new GraphPath(), startIp, 0));
for (int pos : alternateEntries) {
GraphPart e1 = new GraphPart(-1, -1);
e1.path = new GraphPath("e");
ret.add(makeGraph(e1, new GraphPath("e"), code, pos, pos, allBlocks, refs, visited));
//ret.add(makeGraph(e1, new GraphPath("e"), code, pos, pos, allBlocks, refs, visited));
q.offer(new MakeGraphWindow(e1, new GraphPath("e"), pos, pos));
}
loopq: while (!q.isEmpty()) {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
MakeGraphWindow window = q.poll();
GraphPart parent = window.parent;
GraphPath path = window.path;
int startIp = window.startIp;
int lastIp = window.lastIp;
int ip = startIp;
GraphPart existingPart = searchPart(ip, allBlocks);
if (existingPart != null) {
if (parent != null) {
existingPart.refs.add(parent);
parent.nextParts.add(existingPart);
}
//return existingPart;
continue;
}
GraphPart ret = new GraphPart(ip, -1);
ret.path = path;
GraphPart part = ret;
if (parent != null) {
ret.refs.add(parent);
parent.nextParts.add(ret);
}
while (ip < code.size()) {
int aip = checkIp(ip);
if (ip >= code.size()) {
break;
}
if (aip >= code.size()) {
ip = aip;
break;
}
if (visited[ip] || ((ip != startIp) && (refs.get(ip).size() > 1))) {
part.end = lastIp;
GraphPart found = searchPart(aip, allBlocks);
allBlocks.add(part);
if (found != null) {
part.nextParts.add(found);
found.refs.add(part);
break;
} else {
GraphPart nextPart = new GraphPart(aip, -1);
nextPart.path = path;
part.nextParts.add(nextPart);
nextPart.refs.add(part);
part = nextPart;
}
}
visited[ip] = true;
ip = aip;
lastIp = ip;
GraphSourceItem ins = code.get(ip);
if (ins.isIgnored()) {
ip++;
continue;
}
if (ins instanceof GraphSourceItemContainer) {
GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins;
if (ins instanceof Action) { //TODO: Remove dependency of AVM1
long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize();
for (long size : cnt.getContainerSizes()) {
endAddr += size;
}
ip = code.adr2pos(endAddr);
if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) {
part.end = lastIp;
allBlocks.add(part);
GraphPart nextGraphPart = new GraphPart(ip, -1);
nextGraphPart.path = path;
part.nextParts.add(nextGraphPart);
nextGraphPart.refs.add(part);
part = nextGraphPart;
}
}
continue;
} else if (ins.isExit()) {
part.end = ip;
allBlocks.add(part);
break;
} else if (ins.isJump()) {
part.end = ip;
allBlocks.add(part);
ip = ins.getBranches(code).get(0);
ip = checkIp(ip);
//makeGraph(part, path, code, ip, lastIp, allBlocks, refs, visited);
q.offer(new MakeGraphWindow(part, path, ip, lastIp));
lastIp = -1;
break;
} else if (ins.isBranch()) {
part.end = ip;
allBlocks.add(part);
List<Integer> branches = ins.getBranches(code);
for (int i = 0; i < branches.size(); i++) {
//makeGraph(part, path.sub(i, ip), code, checkIp(branches.get(i)), ip, allBlocks, refs, visited);
q.offer(new MakeGraphWindow(part, path.sub(i, ip), checkIp(branches.get(i)), ip));
}
break;
}
ip++;
}
if ((part.end == -1) && (ip >= code.size())) {
if (part.start == code.size()) {
part.end = code.size();
allBlocks.add(part);
} else {
part.end = ip - 1;
for (GraphPart p : allBlocks) {
if (p.start == ip) {
p.refs.add(part);
part.nextParts.add(p);
allBlocks.add(part);
//return ret;
continue loopq;
}
}
GraphPart gp = new GraphPart(ip, ip);
allBlocks.add(gp);
gp.refs.add(part);
part.nextParts.add(gp);
allBlocks.add(part);
}
}
}
gret.add(searchPart(startIp, allBlocks));
for (int pos : alternateEntries) {
gret.add(searchPart(pos, allBlocks));
}
if (Configuration.autoDeobfuscate.get()) {
flattenJumps(ret, allBlocks);
flattenJumps(gret, allBlocks);
}
checkGraph(allBlocks);
return ret;
return gret;
}
/**
* Makes connected set of GraphParts from GraphSource.
*
* @param parent Parent part
* @param path Path
* @param code Graph source
* @param startIp Start IP
* @param lastIp Last IP
* @param allBlocks All blocks
* @param refs References
* @param visited Visited
* @return Entry point
* @throws InterruptedException On interrupt
*/
private GraphPart makeGraph(GraphPart parent, GraphPath path, GraphSource code, int startIp, int lastIp, List<GraphPart> allBlocks, HashMap<Integer, List<Integer>> refs, boolean[] visited) throws InterruptedException {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
private class MakeGraphWindow {
GraphPart parent;
GraphPath path;
int startIp;
int lastIp;
int ip = startIp;
GraphPart existingPart = searchPart(ip, allBlocks);
if (existingPart != null) {
if (parent != null) {
existingPart.refs.add(parent);
parent.nextParts.add(existingPart);
}
return existingPart;
}
GraphPart ret = new GraphPart(ip, -1);
ret.path = path;
GraphPart part = ret;
if (parent != null) {
ret.refs.add(parent);
parent.nextParts.add(ret);
}
while (ip < code.size()) {
int aip = checkIp(ip);
if (ip >= code.size()) {
break;
}
if (aip >= code.size()) {
ip = aip;
break;
}
if (visited[ip] || ((ip != startIp) && (refs.get(ip).size() > 1))) {
part.end = lastIp;
GraphPart found = searchPart(aip, allBlocks);
allBlocks.add(part);
if (found != null) {
part.nextParts.add(found);
found.refs.add(part);
break;
} else {
GraphPart nextPart = new GraphPart(aip, -1);
nextPart.path = path;
part.nextParts.add(nextPart);
nextPart.refs.add(part);
part = nextPart;
}
}
visited[ip] = true;
ip = aip;
lastIp = ip;
GraphSourceItem ins = code.get(ip);
if (ins.isIgnored()) {
ip++;
continue;
}
if (ins instanceof GraphSourceItemContainer) {
GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins;
if (ins instanceof Action) { //TODO: Remove dependency of AVM1
long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize();
for (long size : cnt.getContainerSizes()) {
endAddr += size;
}
ip = code.adr2pos(endAddr);
if ((ins instanceof ActionDefineFunction) || (ins instanceof ActionDefineFunction2)) {
part.end = lastIp;
allBlocks.add(part);
GraphPart nextGraphPart = new GraphPart(ip, -1);
nextGraphPart.path = path;
part.nextParts.add(nextGraphPart);
nextGraphPart.refs.add(part);
part = nextGraphPart;
}
}
continue;
} else if (ins.isExit()) {
part.end = ip;
allBlocks.add(part);
break;
} else if (ins.isJump()) {
part.end = ip;
allBlocks.add(part);
ip = ins.getBranches(code).get(0);
ip = checkIp(ip);
makeGraph(part, path, code, ip, lastIp, allBlocks, refs, visited);
lastIp = -1;
break;
} else if (ins.isBranch()) {
part.end = ip;
allBlocks.add(part);
List<Integer> branches = ins.getBranches(code);
for (int i = 0; i < branches.size(); i++) {
makeGraph(part, path.sub(i, ip), code, checkIp(branches.get(i)), ip, allBlocks, refs, visited);
}
break;
}
ip++;
}
if ((part.end == -1) && (ip >= code.size())) {
if (part.start == code.size()) {
part.end = code.size();
allBlocks.add(part);
} else {
part.end = ip - 1;
for (GraphPart p : allBlocks) {
if (p.start == ip) {
p.refs.add(part);
part.nextParts.add(p);
allBlocks.add(part);
return ret;
}
}
GraphPart gp = new GraphPart(ip, ip);
allBlocks.add(gp);
gp.refs.add(part);
part.nextParts.add(gp);
allBlocks.add(part);
}
}
return ret;
public MakeGraphWindow(GraphPart parent, GraphPath path, int startIp, int lastIp) {
this.parent = parent;
this.path = path;
this.startIp = startIp;
this.lastIp = lastIp;
}
}
/**
* Converts list of TreeItems to string.
*

View File

@@ -60,17 +60,7 @@ public class GraphPart implements Serializable {
/**
* Level in the graph
*/
public int level;
/**
* Discovered time in DFS
*/
public int discoveredTime;
/**
* Finished time in DFS Calculated in setTime.
*/
public int finishedTime;
public int level;
/**
* Closed time. The node is closed when all its input edges are already
@@ -78,57 +68,6 @@ public class GraphPart implements Serializable {
*/
public int closedTime;
/**
* Order in DFS. Calculated in setTime.
*/
public int order;
/**
* Number of parts following this part. Calculated in setNumblocks.
*/
public int numBlocks = Integer.MAX_VALUE;
/**
* Sets the time of this part in DFS.
*
* @param time Time
* @param ordered Ordered parts
* @param visited Visited parts
* @return Time
*/
public int setTime(int time, List<GraphPart> ordered, List<GraphPart> visited) {
if (visited.contains(this)) {
return time;
}
discoveredTime = time;
visited.add(this);
for (GraphPart next : nextParts) {
if (!visited.contains(next)) {
time = next.setTime(time + 1, ordered, visited);
}
}
time++;
finishedTime = time;
order = ordered.size();
ordered.add(this);
return time;
}
/**
* Sets the number of blocks following this part.
*
* @param numBlocks Number of blocks
*/
public void setNumblocks(int numBlocks) {
this.numBlocks = numBlocks;
numBlocks++;
for (GraphPart next : nextParts) {
if (next.numBlocks > numBlocks) {
next.setNumblocks(numBlocks);
}
}
}
/**
* Checks if this part leads to another part.
*

View File

@@ -20,9 +20,11 @@ import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.helpers.CancellableWorker;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Queue;
import java.util.Set;
/**
@@ -91,68 +93,19 @@ public abstract class GraphSource implements Serializable {
*/
public abstract String insToString(int pos);
/**
* Visits the code
*
* @param ip Start position
* @param lastIp Last position
* @param refs References
* @param endIp End position
* @throws InterruptedException On interrupt
*/
private void visitCode(int ip, int lastIp, HashMap<Integer, List<Integer>> refs, int endIp) throws InterruptedException {
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
boolean debugMode = false;
while (((endIp == -1) || (ip < endIp)) && (ip < size())) {
refs.get(ip).add(lastIp);
lastIp = ip;
if (refs.get(ip).size() > 1) {
break;
}
GraphSourceItem ins = get(ip);
if (ins.isIgnored()) {
ip++;
continue;
}
if (debugMode) {
System.err.println("visit ip " + ip + " action:" + ins);
}
if (ins.isExit()) {
break;
}
if (ins instanceof GraphSourceItemContainer) {
GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins;
if (ins instanceof Action) { //TODO: Remove dependency of AVM1
long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize();
for (long size : cnt.getContainerSizes()) {
if (size != 0) {
visitCode(adr2pos(endAddr), ip, refs, adr2pos(endAddr + size));
}
endAddr += size;
}
ip = adr2pos(endAddr);
continue;
}
}
if (ins.isBranch() || ins.isJump()) {
List<Integer> branches = ins.getBranches(this);
for (int b : branches) {
if (b >= 0) {
visitCode(b, ip, refs, endIp);
}
}
break;
}
ip++;
private class VisitCodeWindow {
int ip;
int lastIp;
int endIp;
public VisitCodeWindow(int ip, int lastIp, int endIp) {
this.ip = ip;
this.lastIp = lastIp;
this.endIp = endIp;
}
}
/**
* Visits the code
@@ -167,11 +120,77 @@ public abstract class GraphSource implements Serializable {
for (int i = 0; i < siz; i++) {
refs.put(i, new ArrayList<>());
}
visitCode(startIp, 0, refs, -1);
Queue<VisitCodeWindow> q = new ArrayDeque<>();
q.offer(new VisitCodeWindow(startIp, 0, -1));
//visitCode(startIp, 0, refs, -1);
int pos = 0;
for (int e : alternateEntries) {
pos++;
visitCode(e, -pos, refs, -1);
//visitCode(e, -pos, refs, -1);
q.offer(new VisitCodeWindow(e, -pos, -1));
}
if (CancellableWorker.isInterrupted()) {
throw new InterruptedException();
}
while (!q.isEmpty()) {
VisitCodeWindow window = q.poll();
int ip = window.ip;
int lastIp = window.lastIp;
int endIp = window.endIp;
boolean debugMode = false;
while (((endIp == -1) || (ip < endIp)) && (ip < size())) {
refs.get(ip).add(lastIp);
lastIp = ip;
if (refs.get(ip).size() > 1) {
break;
}
GraphSourceItem ins = get(ip);
if (ins.isIgnored()) {
ip++;
continue;
}
if (debugMode) {
System.err.println("visit ip " + ip + " action:" + ins);
}
if (ins.isExit()) {
break;
}
if (ins instanceof GraphSourceItemContainer) {
GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins;
if (ins instanceof Action) { //TODO: Remove dependency of AVM1
long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize();
for (long size : cnt.getContainerSizes()) {
if (size != 0) {
//visitCode(adr2pos(endAddr), ip, refs, adr2pos(endAddr + size));
q.offer(new VisitCodeWindow(adr2pos(endAddr), ip, adr2pos(endAddr + size)));
}
endAddr += size;
}
ip = adr2pos(endAddr);
continue;
}
}
if (ins.isBranch() || ins.isJump()) {
List<Integer> branches = ins.getBranches(this);
for (int b : branches) {
if (b >= 0) {
q.offer(new VisitCodeWindow(b, ip, endIp));
//visitCode(b, ip, refs, endIp);
}
}
break;
}
ip++;
}
}
return refs;
}

View File

@@ -139,7 +139,7 @@ public class Loop implements Serializable {
}
Set<String> 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);
bcAsStr.add(breakCandidates.get(i) + " - level " + breakCandidatesLevels.get(i));
}
return "loop(id:" + id + (loopPreContinue != null ? ",precontinue:" + loopPreContinue : "") + ",continue:" + loopContinue + ", break:" + loopBreak + ", phase:" + phase + ", backedges: " + String.join(",", edgesAsStr) + ", breakCandidates: " + String.join(",", bcAsStr) + ")";