mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-25 09:06:27 +00:00
perf: optimize recursion, avoid stackoverflow on larger scripts (#2672)
This commit is contained in:
10000
libsrc/ffdec_lib/longs.as
Normal file
10000
libsrc/ffdec_lib/longs.as
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) + ")";
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2026 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash;
|
||||
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.as2.Trait;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
|
||||
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.StringBuilderTextWriter;
|
||||
import com.jpexs.decompiler.flash.tags.DoActionTag;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import static org.testng.Assert.fail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ActionScript2LongTest extends ActionScript2TestBase {
|
||||
@BeforeClass
|
||||
public void init() throws IOException, InterruptedException {
|
||||
//Main.initLogging(false);
|
||||
Configuration.autoDeobfuscate.set(false);
|
||||
Configuration.showAllAddresses.set(false);
|
||||
Configuration.pluginPath.set(null);
|
||||
swf = new SWF(new BufferedInputStream(new FileInputStream("testdata/as2_long/as2_long.swf")), false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongScript() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilderTextWriter writer = new StringBuilderTextWriter(new CodeFormatting(), sb);
|
||||
|
||||
DoActionTag doa = getFirstActionTag();
|
||||
try {
|
||||
Action.actionsToSource(new HashMap<>(),doa, doa.getActions(), "", writer, "UTF-8");
|
||||
} catch (InterruptedException ex) {
|
||||
fail();
|
||||
}
|
||||
|
||||
String result = sb.toString();
|
||||
if (result.contains("/*")) {
|
||||
fail();
|
||||
}
|
||||
if (!result.contains("\"9999\"")) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.abc.types.ConvertData;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
import com.jpexs.decompiler.flash.helpers.CodeFormatting;
|
||||
import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter;
|
||||
import com.jpexs.decompiler.flash.helpers.StringBuilderTextWriter;
|
||||
import com.jpexs.decompiler.flash.tags.DoABC2Tag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import java.io.IOException;
|
||||
@@ -46,6 +47,7 @@ public class ActionScript3ClassTest extends ActionScript3DecompileTestBase {
|
||||
addSwf("assembled", "testdata/as3_assembled/bin/as3_assembled.swf");
|
||||
addSwf("getouterscope", "testdata/getouterscope/getouterscope.swf");
|
||||
addSwf("haxe", "testdata/haxe/output.swf");
|
||||
addSwf("long", "testdata/as3_long/bin/as3_long.flex.swf");
|
||||
}
|
||||
|
||||
private void decompileScriptPack(String swfId, String path, String expectedResult) {
|
||||
@@ -790,4 +792,40 @@ public class ActionScript3ClassTest extends ActionScript3DecompileTestBase {
|
||||
+ "}\n"
|
||||
+ "}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongScript() {
|
||||
DoABC2Tag tag = null;
|
||||
ABC abc = null;
|
||||
ScriptPack scriptPack = null;
|
||||
SWF swf = getSwf("long");
|
||||
for (Tag t : swf.getTags()) {
|
||||
if (t instanceof DoABC2Tag) {
|
||||
tag = (DoABC2Tag) t;
|
||||
abc = tag.getABC();
|
||||
scriptPack = abc.findScriptPackByPath("tests.TestLongScript", Arrays.asList(abc));
|
||||
if (scriptPack != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertNotNull(abc);
|
||||
assertNotNull(scriptPack);
|
||||
StringBuilderTextWriter writer = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
writer = new StringBuilderTextWriter(new CodeFormatting(), sb);
|
||||
scriptPack.toSource(swf.getAbcIndex(), writer, abc.script_info.get(scriptPack.scriptIndex).traits.traits, new ConvertData(), ScriptExportMode.AS, false, false, false);
|
||||
} catch (InterruptedException ex) {
|
||||
fail();
|
||||
}
|
||||
|
||||
String result = sb.toString();
|
||||
if (result.contains("/*")) {
|
||||
fail();
|
||||
}
|
||||
if (!result.contains("\"9999\"")) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
libsrc/ffdec_lib/testdata/as2_long/as2_long.html
vendored
Normal file
49
libsrc/ffdec_lib/testdata/as2_long/as2_long.html
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<title>as2_long</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css" media="screen">
|
||||
html, body { height:100%; background-color: #ffffff;}
|
||||
body { margin:0; padding:0; overflow:hidden; }
|
||||
#flashContent { width:100%; height:100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="flashContent">
|
||||
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="550" height="400" id="as2_long" align="middle">
|
||||
<param name="movie" value="as2_long.swf" />
|
||||
<param name="quality" value="high" />
|
||||
<param name="bgcolor" value="#ffffff" />
|
||||
<param name="play" value="true" />
|
||||
<param name="loop" value="true" />
|
||||
<param name="wmode" value="window" />
|
||||
<param name="scale" value="showall" />
|
||||
<param name="menu" value="true" />
|
||||
<param name="devicefont" value="false" />
|
||||
<param name="salign" value="" />
|
||||
<param name="allowScriptAccess" value="sameDomain" />
|
||||
<!--[if !IE]>-->
|
||||
<object type="application/x-shockwave-flash" data="as2_long.swf" width="550" height="400">
|
||||
<param name="movie" value="as2_long.swf" />
|
||||
<param name="quality" value="high" />
|
||||
<param name="bgcolor" value="#ffffff" />
|
||||
<param name="play" value="true" />
|
||||
<param name="loop" value="true" />
|
||||
<param name="wmode" value="window" />
|
||||
<param name="scale" value="showall" />
|
||||
<param name="menu" value="true" />
|
||||
<param name="devicefont" value="false" />
|
||||
<param name="salign" value="" />
|
||||
<param name="allowScriptAccess" value="sameDomain" />
|
||||
<!--<![endif]-->
|
||||
<a href="http://www.adobe.com/go/getflash">
|
||||
<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" />
|
||||
</a>
|
||||
<!--[if !IE]>-->
|
||||
</object>
|
||||
<!--<![endif]-->
|
||||
</object>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
libsrc/ffdec_lib/testdata/as2_long/as2_long.swf
vendored
Normal file
BIN
libsrc/ffdec_lib/testdata/as2_long/as2_long.swf
vendored
Normal file
Binary file not shown.
10023
libsrc/ffdec_lib/testdata/as2_long/as2_long/DOMDocument.xml
vendored
Normal file
10023
libsrc/ffdec_lib/testdata/as2_long/as2_long/DOMDocument.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
55
libsrc/ffdec_lib/testdata/as2_long/as2_long/META-INF/metadata.xml
vendored
Normal file
55
libsrc/ffdec_lib/testdata/as2_long/as2_long/META-INF/metadata.xml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 ">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
|
||||
<xmp:CreatorTool>Adobe Flash Professional CS6 - build 481</xmp:CreatorTool>
|
||||
<xmp:CreateDate>2026-03-19T23:26:42-07:00</xmp:CreateDate>
|
||||
<xmp:MetadataDate>2026-03-19T23:27:45-07:00</xmp:MetadataDate>
|
||||
<xmp:ModifyDate>2026-03-19T23:27:45-07:00</xmp:ModifyDate>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:format>application/vnd.adobe.fla</dc:format>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
|
||||
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#">
|
||||
<xmpMM:InstanceID>xmp.iid:E30104E82524F1119CEDF7FC46CFB7AB</xmpMM:InstanceID>
|
||||
<xmpMM:DocumentID>xmp.did:E30104E82524F1119CEDF7FC46CFB7AB</xmpMM:DocumentID>
|
||||
<xmpMM:OriginalDocumentID>xmp.did:E30104E82524F1119CEDF7FC46CFB7AB</xmpMM:OriginalDocumentID>
|
||||
<xmpMM:History>
|
||||
<rdf:Seq>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<stEvt:action>created</stEvt:action>
|
||||
<stEvt:instanceID>xmp.iid:E30104E82524F1119CEDF7FC46CFB7AB</stEvt:instanceID>
|
||||
<stEvt:when>2026-03-19T23:26:42-07:00</stEvt:when>
|
||||
<stEvt:softwareAgent>Adobe Flash Professional CS6 - build 481</stEvt:softwareAgent>
|
||||
</rdf:li>
|
||||
</rdf:Seq>
|
||||
</xmpMM:History>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<?xpacket end="w"?>
|
||||
0
libsrc/ffdec_lib/testdata/as2_long/as2_long/MobileSettings.xml
vendored
Normal file
0
libsrc/ffdec_lib/testdata/as2_long/as2_long/MobileSettings.xml
vendored
Normal file
206
libsrc/ffdec_lib/testdata/as2_long/as2_long/PublishSettings.xml
vendored
Normal file
206
libsrc/ffdec_lib/testdata/as2_long/as2_long/PublishSettings.xml
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<flash_profiles>
|
||||
<flash_profile version="1.0" name="Default" current="true">
|
||||
<PublishFormatProperties enabled="true">
|
||||
<defaultNames>1</defaultNames>
|
||||
<flash>1</flash>
|
||||
<projectorWin>0</projectorWin>
|
||||
<projectorMac>0</projectorMac>
|
||||
<html>1</html>
|
||||
<gif>0</gif>
|
||||
<jpeg>0</jpeg>
|
||||
<png>0</png>
|
||||
<qt>0</qt>
|
||||
<rnwk>0</rnwk>
|
||||
<swc>0</swc>
|
||||
<flashDefaultName>1</flashDefaultName>
|
||||
<projectorWinDefaultName>1</projectorWinDefaultName>
|
||||
<projectorMacDefaultName>1</projectorMacDefaultName>
|
||||
<htmlDefaultName>1</htmlDefaultName>
|
||||
<gifDefaultName>1</gifDefaultName>
|
||||
<jpegDefaultName>1</jpegDefaultName>
|
||||
<pngDefaultName>1</pngDefaultName>
|
||||
<qtDefaultName>1</qtDefaultName>
|
||||
<rnwkDefaultName>1</rnwkDefaultName>
|
||||
<swcDefaultName>1</swcDefaultName>
|
||||
<flashFileName>Untitled-1.swf</flashFileName>
|
||||
<projectorWinFileName>Untitled-1.exe</projectorWinFileName>
|
||||
<projectorMacFileName>Untitled-1.app</projectorMacFileName>
|
||||
<htmlFileName>Untitled-1.html</htmlFileName>
|
||||
<gifFileName>Untitled-1.gif</gifFileName>
|
||||
<jpegFileName>Untitled-1.jpg</jpegFileName>
|
||||
<pngFileName>Untitled-1.png</pngFileName>
|
||||
<qtFileName>Untitled-1.mov</qtFileName>
|
||||
<rnwkFileName>Untitled-1.smil</rnwkFileName>
|
||||
<swcFileName>Untitled-1.swc</swcFileName>
|
||||
</PublishFormatProperties>
|
||||
<PublishHtmlProperties enabled="true">
|
||||
<VersionDetectionIfAvailable>0</VersionDetectionIfAvailable>
|
||||
<VersionInfo>12,0,0,0;11,2,0,0;11,1,0,0;10,3,0,0;10,2,153,0;10,1,52,0;9,0,124,0;8,0,24,0;7,0,14,0;6,0,79,0;5,0,58,0;4,0,32,0;3,0,8,0;2,0,1,12;1,0,0,1;</VersionInfo>
|
||||
<UsingDefaultContentFilename>1</UsingDefaultContentFilename>
|
||||
<UsingDefaultAlternateFilename>1</UsingDefaultAlternateFilename>
|
||||
<ContentFilename>Untitled-1_content.html</ContentFilename>
|
||||
<AlternateFilename>Untitled-1_alternate.html</AlternateFilename>
|
||||
<UsingOwnAlternateFile>0</UsingOwnAlternateFile>
|
||||
<OwnAlternateFilename></OwnAlternateFilename>
|
||||
<Width>550</Width>
|
||||
<Height>400</Height>
|
||||
<Align>0</Align>
|
||||
<Units>0</Units>
|
||||
<Loop>1</Loop>
|
||||
<StartPaused>0</StartPaused>
|
||||
<Scale>0</Scale>
|
||||
<HorizontalAlignment>1</HorizontalAlignment>
|
||||
<VerticalAlignment>1</VerticalAlignment>
|
||||
<Quality>4</Quality>
|
||||
<DeblockingFilter>0</DeblockingFilter>
|
||||
<WindowMode>0</WindowMode>
|
||||
<DisplayMenu>1</DisplayMenu>
|
||||
<DeviceFont>0</DeviceFont>
|
||||
<TemplateFileName>C:\Users\MyUser\AppData\Local\Adobe\Flash CS6\en_US\Configuration\HTML\Default.html</TemplateFileName>
|
||||
<showTagWarnMsg>1</showTagWarnMsg>
|
||||
</PublishHtmlProperties>
|
||||
<PublishFlashProperties enabled="true">
|
||||
<TopDown></TopDown>
|
||||
<FireFox></FireFox>
|
||||
<Report>0</Report>
|
||||
<Protect>0</Protect>
|
||||
<OmitTraceActions>0</OmitTraceActions>
|
||||
<Quality>80</Quality>
|
||||
<DeblockingFilter>0</DeblockingFilter>
|
||||
<StreamFormat>0</StreamFormat>
|
||||
<StreamCompress>7</StreamCompress>
|
||||
<EventFormat>0</EventFormat>
|
||||
<EventCompress>7</EventCompress>
|
||||
<OverrideSounds>0</OverrideSounds>
|
||||
<Version>15</Version>
|
||||
<ExternalPlayer>FlashPlayer11.2</ExternalPlayer>
|
||||
<ActionScriptVersion>2</ActionScriptVersion>
|
||||
<PackageExportFrame>1</PackageExportFrame>
|
||||
<PackagePaths></PackagePaths>
|
||||
<AS3PackagePaths>.</AS3PackagePaths>
|
||||
<AS3ConfigConst>CONFIG::FLASH_AUTHORING="true";</AS3ConfigConst>
|
||||
<DebuggingPermitted>0</DebuggingPermitted>
|
||||
<DebuggingPassword></DebuggingPassword>
|
||||
<CompressMovie>1</CompressMovie>
|
||||
<CompressionType>0</CompressionType>
|
||||
<InvisibleLayer>1</InvisibleLayer>
|
||||
<DeviceSound>0</DeviceSound>
|
||||
<StreamUse8kSampleRate>0</StreamUse8kSampleRate>
|
||||
<EventUse8kSampleRate>0</EventUse8kSampleRate>
|
||||
<UseNetwork>0</UseNetwork>
|
||||
<DocumentClass></DocumentClass>
|
||||
<AS3Strict>2</AS3Strict>
|
||||
<AS3Coach>4</AS3Coach>
|
||||
<AS3AutoDeclare>4096</AS3AutoDeclare>
|
||||
<AS3Dialect>AS3</AS3Dialect>
|
||||
<AS3ExportFrame>1</AS3ExportFrame>
|
||||
<AS3Optimize>1</AS3Optimize>
|
||||
<ExportSwc>0</ExportSwc>
|
||||
<ScriptStuckDelay>15</ScriptStuckDelay>
|
||||
<IncludeXMP>1</IncludeXMP>
|
||||
<HardwareAcceleration>0</HardwareAcceleration>
|
||||
<AS3Flags>4102</AS3Flags>
|
||||
<DefaultLibraryLinkage>rsl</DefaultLibraryLinkage>
|
||||
<RSLPreloaderMethod>wrap</RSLPreloaderMethod>
|
||||
<RSLPreloaderSWF>$(AppConfig)/ActionScript 3.0/rsls/loader_animation.swf</RSLPreloaderSWF>
|
||||
<LibraryPath>
|
||||
<library-path-entry>
|
||||
<swc-path>$(AppConfig)/ActionScript 3.0/libs</swc-path>
|
||||
<linkage>merge</linkage>
|
||||
</library-path-entry>
|
||||
<library-path-entry>
|
||||
<swc-path>$(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc</swc-path>
|
||||
<linkage usesDefault="true">rsl</linkage>
|
||||
<rsl-url>http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz</rsl-url>
|
||||
<policy-file-url>http://fpdownload.adobe.com/pub/swz/crossdomain.xml</policy-file-url>
|
||||
<rsl-url>textLayout_2.0.0.232.swz</rsl-url>
|
||||
</library-path-entry>
|
||||
</LibraryPath>
|
||||
<LibraryVersions>
|
||||
<library-version>
|
||||
<swc-path>$(AppConfig)/ActionScript 3.0/libs/11.0/textLayout.swc</swc-path>
|
||||
<feature name="tlfText" majorVersion="2" minorVersion="0" build="232"/>
|
||||
<rsl-url>http://fpdownload.adobe.com/pub/swz/tlf/2.0.0.232/textLayout_2.0.0.232.swz</rsl-url>
|
||||
<policy-file-url>http://fpdownload.adobe.com/pub/swz/crossdomain.xml</policy-file-url>
|
||||
<rsl-url>textLayout_2.0.0.232.swz</rsl-url>
|
||||
</library-version>
|
||||
</LibraryVersions>
|
||||
</PublishFlashProperties>
|
||||
<PublishJpegProperties enabled="true">
|
||||
<Width>550</Width>
|
||||
<Height>400</Height>
|
||||
<Progressive>0</Progressive>
|
||||
<DPI>4718592</DPI>
|
||||
<Size>0</Size>
|
||||
<Quality>80</Quality>
|
||||
<MatchMovieDim>1</MatchMovieDim>
|
||||
</PublishJpegProperties>
|
||||
<PublishRNWKProperties enabled="true">
|
||||
<exportFlash>1</exportFlash>
|
||||
<flashBitRate>0</flashBitRate>
|
||||
<exportAudio>1</exportAudio>
|
||||
<audioFormat>0</audioFormat>
|
||||
<singleRateAudio>0</singleRateAudio>
|
||||
<realVideoRate>100000</realVideoRate>
|
||||
<speed28K>1</speed28K>
|
||||
<speed56K>1</speed56K>
|
||||
<speedSingleISDN>0</speedSingleISDN>
|
||||
<speedDualISDN>0</speedDualISDN>
|
||||
<speedCorporateLAN>0</speedCorporateLAN>
|
||||
<speed256K>0</speed256K>
|
||||
<speed384K>0</speed384K>
|
||||
<speed512K>0</speed512K>
|
||||
<exportSMIL>1</exportSMIL>
|
||||
</PublishRNWKProperties>
|
||||
<PublishGifProperties enabled="true">
|
||||
<Width>550</Width>
|
||||
<Height>400</Height>
|
||||
<Animated>0</Animated>
|
||||
<MatchMovieDim>1</MatchMovieDim>
|
||||
<Loop>1</Loop>
|
||||
<LoopCount></LoopCount>
|
||||
<OptimizeColors>1</OptimizeColors>
|
||||
<Interlace>0</Interlace>
|
||||
<Smooth>1</Smooth>
|
||||
<DitherSolids>0</DitherSolids>
|
||||
<RemoveGradients>0</RemoveGradients>
|
||||
<TransparentOption></TransparentOption>
|
||||
<TransparentAlpha>128</TransparentAlpha>
|
||||
<DitherOption></DitherOption>
|
||||
<PaletteOption></PaletteOption>
|
||||
<MaxColors>255</MaxColors>
|
||||
<PaletteName></PaletteName>
|
||||
</PublishGifProperties>
|
||||
<PublishPNGProperties enabled="true">
|
||||
<Width>550</Width>
|
||||
<Height>400</Height>
|
||||
<OptimizeColors>1</OptimizeColors>
|
||||
<Interlace>0</Interlace>
|
||||
<Transparent>0</Transparent>
|
||||
<Smooth>1</Smooth>
|
||||
<DitherSolids>0</DitherSolids>
|
||||
<RemoveGradients>0</RemoveGradients>
|
||||
<MatchMovieDim>1</MatchMovieDim>
|
||||
<DitherOption></DitherOption>
|
||||
<FilterOption></FilterOption>
|
||||
<PaletteOption></PaletteOption>
|
||||
<BitDepth>24-bit with Alpha</BitDepth>
|
||||
<MaxColors>255</MaxColors>
|
||||
<PaletteName></PaletteName>
|
||||
</PublishPNGProperties>
|
||||
<PublishQTProperties enabled="true">
|
||||
<Width>550</Width>
|
||||
<Height>400</Height>
|
||||
<MatchMovieDim>1</MatchMovieDim>
|
||||
<UseQTSoundCompression>0</UseQTSoundCompression>
|
||||
<AlphaOption></AlphaOption>
|
||||
<LayerOption></LayerOption>
|
||||
<QTSndSettings>00000000</QTSndSettings>
|
||||
<ControllerOption>0</ControllerOption>
|
||||
<Looping>0</Looping>
|
||||
<PausedAtStart>0</PausedAtStart>
|
||||
<PlayEveryFrame>0</PlayEveryFrame>
|
||||
<Flatten>1</Flatten>
|
||||
</PublishQTProperties>
|
||||
</flash_profile>
|
||||
</flash_profiles>
|
||||
1
libsrc/ffdec_lib/testdata/as2_long/as2_long/as2_long.xfl
vendored
Normal file
1
libsrc/ffdec_lib/testdata/as2_long/as2_long/as2_long.xfl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
PROXY-CS5
|
||||
BIN
libsrc/ffdec_lib/testdata/as2_long/as2_long/bin/SymDepend.cache
vendored
Normal file
BIN
libsrc/ffdec_lib/testdata/as2_long/as2_long/bin/SymDepend.cache
vendored
Normal file
Binary file not shown.
BIN
libsrc/ffdec_lib/testdata/as3_long/bin/as3_long.air.swf
vendored
Normal file
BIN
libsrc/ffdec_lib/testdata/as3_long/bin/as3_long.air.swf
vendored
Normal file
Binary file not shown.
BIN
libsrc/ffdec_lib/testdata/as3_long/bin/as3_long.flex.swf
vendored
Normal file
BIN
libsrc/ffdec_lib/testdata/as3_long/bin/as3_long.flex.swf
vendored
Normal file
Binary file not shown.
7
libsrc/ffdec_lib/testdata/as3_long/build_air_debug.bat
vendored
Normal file
7
libsrc/ffdec_lib/testdata/as3_long/build_air_debug.bat
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
set COMPILERKIND=air
|
||||
set SWFNAME=as3_long
|
||||
call c:\air\bin\mxmlc.bat -debug=true -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1
|
||||
rem set COMPILERKIND=air.optimize
|
||||
rem call c:\air\bin\mxmlc.bat -compiler.optimize -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1
|
||||
rem -warnings=false
|
||||
3
libsrc/ffdec_lib/testdata/as3_long/build_both_debug.bat
vendored
Normal file
3
libsrc/ffdec_lib/testdata/as3_long/build_both_debug.bat
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
call build_flex_debug.bat
|
||||
call build_air_debug.bat
|
||||
7
libsrc/ffdec_lib/testdata/as3_long/build_flex_debug.bat
vendored
Normal file
7
libsrc/ffdec_lib/testdata/as3_long/build_flex_debug.bat
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
set COMPILERKIND=flex
|
||||
set SWFNAME=as3_long
|
||||
call c:\flex\bin\mxmlc.bat -debug=true -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1
|
||||
rem set COMPILERKIND=flex.optimize
|
||||
rem call c:\flex\bin\mxmlc.bat -compiler.optimize -output bin/%SWFNAME%.%COMPILERKIND%.swf src/Main.as 1> buildlog.%COMPILERKIND%.txt 2>&1
|
||||
rem -warnings=false
|
||||
28
libsrc/ffdec_lib/testdata/as3_long/src/Main.as
vendored
Normal file
28
libsrc/ffdec_lib/testdata/as3_long/src/Main.as
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package
|
||||
{
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import tests.*;
|
||||
/**
|
||||
* ...
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Main extends Sprite
|
||||
{
|
||||
TestLongScript;
|
||||
|
||||
public function Main()
|
||||
{
|
||||
if (stage) init();
|
||||
else addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
}
|
||||
|
||||
private function init(e:Event = null):void
|
||||
{
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
// entry point
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
10013
libsrc/ffdec_lib/testdata/as3_long/src/tests/TestLongScript.as
vendored
Normal file
10013
libsrc/ffdec_lib/testdata/as3_long/src/tests/TestLongScript.as
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user