Stack handling improved - no more StackEmptyException

And/Or handling improved
Preprocessor instructions introduced - §§pop,§§push...
This commit is contained in:
Jindra Petřík
2015-06-03 10:10:44 +02:00
parent 8e770dad9f
commit cac19d6cb9
84 changed files with 11171 additions and 11238 deletions

View File

@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.graph;
import com.jpexs.decompiler.graph.model.PushItem;
import com.jpexs.decompiler.graph.model.PopItem;
import com.jpexs.decompiler.flash.BaseLocalData;
import com.jpexs.decompiler.flash.FinalProcessLocalData;
import com.jpexs.decompiler.flash.action.Action;
@@ -25,7 +27,9 @@ import com.jpexs.decompiler.graph.model.AndItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.DoWhileItem;
import com.jpexs.decompiler.graph.model.DuplicateItem;
import com.jpexs.decompiler.graph.model.ExitItem;
import com.jpexs.decompiler.graph.model.FalseItem;
import com.jpexs.decompiler.graph.model.ForItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.IntegerValueItem;
@@ -37,8 +41,10 @@ import com.jpexs.decompiler.graph.model.OrItem;
import com.jpexs.decompiler.graph.model.ScriptEndItem;
import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.TrueItem;
import com.jpexs.decompiler.graph.model.UniversalLoopItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import com.jpexs.helpers.Helper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -422,7 +428,7 @@ public class Graph {
for (GraphPart head : heads) {
populateParts(head, allParts);
}
TranslateStack stack = new TranslateStack();
TranslateStack stack = new TranslateStack(path);
List<Loop> loops = new ArrayList<>();
getLoops(localData, heads.get(0), loops, null);
/*System.out.println("<loops>");
@@ -461,8 +467,59 @@ public class Graph {
finalProcessAfter(list, level, localData);
}
protected void finalProcessAfter(List<GraphTargetItem> list, int level, FinalProcessLocalData localData) {
private boolean processSubBlk(Block b, GraphTargetItem replacement) {
boolean allSubPush = true;
boolean atleastOne = false;
for (List<GraphTargetItem> sub : b.getSubs()) {
if (!sub.isEmpty()) {
int lastPos = sub.size() - 1;
GraphTargetItem last = sub.get(sub.size() - 1);
GraphTargetItem br = null;
if ((last instanceof BreakItem) && (sub.size() >= 2)) {
br = last;
lastPos--;
last = sub.get(lastPos);
}
if (last instanceof Block) {
if (!processSubBlk((Block) last, replacement)) {
allSubPush = false;
} else {
atleastOne = true;
}
} else if (last instanceof PushItem) {
if (replacement != null) {
GraphTargetItem e2 = (((GraphTargetItem) replacement).clone());
e2.value = last.value;
sub.set(lastPos, e2);
if (br != null) {
sub.remove(sub.size() - 1);
}
}
atleastOne = true;
} else if (!(last instanceof ExitItem)) {
allSubPush = false;
}
}
}
return allSubPush && atleastOne;
}
protected void finalProcessAfter(List<GraphTargetItem> list, int level, FinalProcessLocalData localData) {
if (list.size() >= 2) {
if (list.get(list.size() - 1) instanceof ExitItem) {
ExitItem e = (ExitItem) list.get(list.size() - 1);
if (list.get(list.size() - 1).value instanceof PopItem) {
if (list.get(list.size() - 2) instanceof Block) {
Block b = (Block) list.get(list.size() - 2);
if (processSubBlk(b, (GraphTargetItem) e)) {
list.remove(list.size() - 1);
}
}
}
}
}
}
protected void finalProcess(List<GraphTargetItem> list, int level, FinalProcessLocalData localData) {
@@ -527,7 +584,7 @@ public class Graph {
}
private void processIfs(List<GraphTargetItem> list) {
//if(true) return;
for (int i = 0; i < list.size(); i++) {
GraphTargetItem item = list.get(i);
if (item instanceof Block) {
@@ -1382,7 +1439,11 @@ public class Graph {
}
List<GraphTargetItem> currentRet = ret;
UniversalLoopItem loopItem = null;
TranslateStack sPreLoop = stack;
if (isLoop) {
//makeAllCommands(currentRet, stack);
stack = (TranslateStack) stack.clone();
stack.clear();
loopItem = new UniversalLoopItem(null, currentLoop);
//loopItem.commands=printGraph(visited, localData, stack, allParts, parent, part, stopPart, loops);
currentRet.add(loopItem);
@@ -1412,23 +1473,6 @@ public class Graph {
}
}
//Assuming part with two nextparts is an IF
/* //If with both branches empty
if (part.nextParts.size() == 2) {
if (part.nextParts.get(0) == part.nextParts.get(1)) {
if (!stack.isEmpty()) {
GraphTargetItem expr = stack.pop();
if (expr instanceof LogicalOpItem) {
expr = ((LogicalOpItem) expr).invert();
} else {
expr = new NotItem(null, expr);
}
output.add(new IfItem(null, expr, new ArrayList<GraphTargetItem>(), new ArrayList<GraphTargetItem>()));
}
part.nextParts.remove(0);
}
}*/
if (parseNext) {
List<GraphTargetItem> retCheck = check(code, localData, allParts, stack, parent, part, stopPart, loops, output, currentLoop, staticOperation, path);
if (retCheck != null) {
@@ -1441,134 +1485,15 @@ public class Graph {
currentRet.addAll(output);
}
}
/**
* AND / OR detection
*/
if (parseNext && part.nextParts.size() == 2) {
if ((stack.size() >= 2) && (stack.get(stack.size() - 1) instanceof NotItem) && (((NotItem) (stack.get(stack.size() - 1))).getOriginal().getNotCoerced() == stack.get(stack.size() - 2).getNotCoerced())) {
GraphPart sp0 = getNextNoJump(part.nextParts.get(0), localData);
GraphPart sp1 = getNextNoJump(part.nextParts.get(1), localData);
boolean reversed = false;
loopContinues = getLoopsContinues(loops);
loopContinues.add(part);//???
if (sp1.leadsTo(localData, this, code, sp0, loops)) {
} else if (sp0.leadsTo(localData, this, code, sp1, loops)) {
reversed = true;
}
GraphPart next = reversed ? sp0 : sp1;
GraphTargetItem ti;
if ((ti = checkLoop(next, stopPart, loops)) != null) {
currentRet.add(ti);
} else {
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
GraphPart andOrStopPart = reversed ? sp1 : sp0;
stopPart2.add(andOrStopPart);
GraphTargetItem first = ((NotItem) stack.pop()).getOriginal();
stack.pop();
stack.push(new MarkItem("disposable"));
printGraph(visited, localData, stack, allParts, parent, next, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
GraphTargetItem second = stack.pop();
if (!reversed) {
AndItem a = new AndItem(null, first, second);
stack.push(a);
a.firstPart = part;
if (second instanceof AndItem) {
a.firstPart = ((AndItem) second).firstPart;
}
if (second instanceof OrItem) {
a.firstPart = ((OrItem) second).firstPart;
}
} else {
OrItem o = new OrItem(null, first, second);
stack.push(o);
o.firstPart = part;
if (second instanceof AndItem) {
o.firstPart = ((AndItem) second).firstPart;
}
if (second instanceof OrItem) {
o.firstPart = ((OrItem) second).firstPart;
}
}
next = reversed ? sp1 : sp0;
if ((ti = checkLoop(next, stopPart, loops)) != null) {
currentRet.add(ti);
} else {
currentRet.addAll(printGraph(visited, localData, stack, allParts, parent, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1));
}
}
parseNext = false;
//return ret;
} else if ((stack.size() >= 2) && (stack.get(stack.size() - 1).getNotCoerced() == stack.get(stack.size() - 2).getNotCoerced())) {
GraphPart sp0 = getNextNoJump(part.nextParts.get(0), localData);
GraphPart sp1 = getNextNoJump(part.nextParts.get(1), localData);
boolean reversed = false;
loopContinues = getLoopsContinues(loops);
loopContinues.add(part);//???
if (sp1.leadsTo(localData, this, code, sp0, loops)) {
} else if (sp0.leadsTo(localData, this, code, sp1, loops)) {
reversed = true;
}
GraphPart next = reversed ? sp0 : sp1;
GraphTargetItem ti;
if ((ti = checkLoop(next, stopPart, loops)) != null) {
currentRet.add(ti);
} else {
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
GraphPart andOrStopPart = reversed ? sp1 : sp0;
//andOrStopPart.stopPartType = GraphPart.StopPartType.AND_OR;
stopPart2.add(andOrStopPart);
GraphTargetItem first = stack.pop();
stack.pop();
stack.push(new MarkItem("disposable"));
printGraph(visited, localData, stack, allParts, parent, next, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
//stack = andOrStopPart.andOrStack; // Use stack that was stored upon reaching AND_OR stopPart
GraphTargetItem second = stack.pop();
//GraphTargetItem first = stack.pop();
//andOrStopPart.stopPartType = GraphPart.StopPartType.NONE; // Reset stopPartType
if (reversed) {
AndItem a = new AndItem(null, first, second);
stack.push(a);
a.firstPart = part;
if (second instanceof AndItem) {
a.firstPart = ((AndItem) second).firstPart;
}
if (second instanceof OrItem) {
a.firstPart = ((OrItem) second).firstPart;
}
} else {
OrItem o = new OrItem(null, first, second);
stack.push(o);
o.firstPart = part;
if (second instanceof OrItem) {
o.firstPart = ((OrItem) second).firstPart;
}
if (second instanceof AndItem) {
o.firstPart = ((AndItem) second).firstPart;
}
}
next = reversed ? sp1 : sp0;
if ((ti = checkLoop(next, stopPart, loops)) != null) {
currentRet.add(ti);
} else {
currentRet.addAll(printGraph(visited, localData, stack, allParts, parent, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1));
}
}
parseNext = false;
//return ret;
}
}
//********************************END PART DECOMPILING
if (parseNext) {
if (part.nextParts.size() > 2) {//direct switch, seen in the wild...
GraphPart next = getMostCommonPart(localData, part.nextParts, loops);
List<GraphPart> vis = new ArrayList<>();
GraphTargetItem switchedItem = stack.pop();
makeAllCommands(currentRet, stack);
List<GraphTargetItem> caseValues = new ArrayList<>();
List<List<GraphTargetItem>> caseCommands = new ArrayList<>();
List<GraphTargetItem> defaultCommands = new ArrayList<>();
@@ -1604,10 +1529,15 @@ public class Graph {
}
}
if (next != p) {
//int stackLenBefore = stack.size();
TranslateStack s2 = (TranslateStack) stack.clone();
s2.clear();
List<GraphTargetItem> nextCommands = printGraph(visited, prepareBranchLocalData(localData), s2, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
makeAllCommands(nextCommands, s2);
if (first) {
defaultCommands = printGraph(visited, prepareBranchLocalData(localData), stack, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
defaultCommands = nextCommands;
} else {
caseCommands.add(printGraph(visited, prepareBranchLocalData(localData), stack, allParts, part, p, stopPart2, loops, null, staticOperation, path, recursionLevel + 1));
caseCommands.add(nextCommands);
}
vis.add(p);
}
@@ -1623,41 +1553,23 @@ public class Graph {
GraphPart nextOnePart = null;
if (part.nextParts.size() == 2) {
GraphTargetItem expr = stack.pop();
if (expr instanceof LogicalOpItem) {
expr = ((LogicalOpItem) expr).invert();
} else {
expr = new NotItem(null, expr);
}
if (staticOperation != SOP_USE_STATIC) {
if (expr.isCompileTime()) {
boolean doJump = EcmaScript.toBoolean(expr.getResult());
if (doJump) {
nextOnePart = part.nextParts.get(0);
} else {
nextOnePart = part.nextParts.get(1);
}
if (staticOperation == SOP_REMOVE_STATIC) {
//TODO
}
}
}
/*if (expr instanceof LogicalOpItem) {
expr = ((LogicalOpItem) expr).invert();
} else {
expr = new NotItem(null, expr);
}*/
if (nextOnePart == null) {
List<GraphPart> nps;
/*nps = new ArrayList<>(part.nextParts);
for(int i=0;i<nps.size();i++){
nps.set(i,getNextNoJump(nps.get(i),localData));
}
if(nps.get(0) == nps.get(1)){
nps = part.nextParts;
}*/
nps = part.nextParts;
GraphPart next = getCommonPart(localData, nps, loops);
TranslateStack trueStack = (TranslateStack) stack.clone();
TranslateStack falseStack = (TranslateStack) stack.clone();
int trueStackSizeBefore = trueStack.size();
int falseStackSizeBefore = falseStack.size();
List<GraphTargetItem> onTrue = new ArrayList<>();
trueStack.clear();
falseStack.clear();
/*int trueStackSizeBefore = trueStack.size();
int falseStackSizeBefore = falseStack.size();
*/
boolean isEmpty = nps.get(0) == nps.get(1);
if (isEmpty) {
@@ -1665,59 +1577,63 @@ public class Graph {
}
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
//GraphPart.CommonPartStack commonPartStack = null;
if ((!isEmpty) && (next != null)) {
/*commonPartStack = next.new CommonPartStack();
if (next.commonPartStacks == null) {
next.commonPartStacks = new ArrayList<>();
}
next.stopPartType = GraphPart.StopPartType.COMMONPART;
*/
stopPart2.add(next);
}
List<GraphTargetItem> onTrue = new ArrayList<>();
if (!isEmpty) {
onTrue = printGraph(visited, prepareBranchLocalData(localData), trueStack, allParts, part, nps.get(1), stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
}
List<GraphTargetItem> onFalse = new ArrayList<>();
if (!isEmpty) {
/*if (next != null) {
commonPartStack.isTrueStack = false; //stopPart must know it needs to store falseStack
}*/
onFalse = printGraph(visited, prepareBranchLocalData(localData), falseStack, allParts, part, nps.get(0), stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
}
//List<GraphTargetItem> out2 = new ArrayList<>();
//makeAllCommands(out2, stack);
makeAllCommands(onTrue, trueStack);
makeAllCommands(onFalse, falseStack);
/* if there is a stopPart (next), then Graph will be further analyzed starting from the stopPart:
* trueStack and falseStack must be set equal to corresponding stack that was built upon reaching stopPart.
if ((!isEmpty) && (next != null)) {
if ((commonPartStack.trueStack != null) && (commonPartStack.falseStack != null)) {
trueStack = commonPartStack.trueStack;
falseStack = commonPartStack.falseStack;
}
next.commonPartStacks.remove(next.commonPartStacks.size() - 1);
if (next.commonPartStacks.isEmpty()) {
next.stopPartType = GraphPart.StopPartType.NONE; // reset StopPartType
}
}*/
if (isEmpty(onTrue) && isEmpty(onFalse) && (trueStack.size() == trueStackSizeBefore + 1) && (falseStack.size() == falseStackSizeBefore + 1)) {
stack.push(new TernarOpItem(null, expr, trueStack.pop(), falseStack.pop()));
if (!isEmpty(onTrue) && !isEmpty(onFalse) && onTrue.size() == 1 && onFalse.size() == 1 && (onTrue.get(0) instanceof PushItem) && (onFalse.get(0) instanceof PushItem)) {
stack.push(new TernarOpItem(null, expr.invert(null), ((PushItem) onTrue.get(0)).value, ((PushItem) onFalse.get(0)).value));
} else {
currentRet.add(new IfItem(null, expr, onTrue, onFalse));
}
if (next != null) {
if (trueStack.size() != trueStackSizeBefore || falseStack.size() != falseStackSizeBefore) {
// it's a hack, because duplicates all instructions in the next part, but better than EmptyStackException
onTrue = printGraph(visited, localData, trueStack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1);
onFalse = printGraph(visited, localData, falseStack, allParts, part, next, stopPart, loops, null, staticOperation, path, recursionLevel + 1);
if (isEmpty(onTrue) && isEmpty(onFalse) && (trueStack.size() == trueStackSizeBefore + 1) && (falseStack.size() == falseStackSizeBefore + 1)) {
stack.push(new TernarOpItem(null, expr, trueStack.pop(), falseStack.pop()));
boolean isIf = true;
if (!stack.isEmpty() && onFalse.isEmpty() && onTrue.size() == 2 && (onTrue.get(0) instanceof PopItem) && (onTrue.get(1) instanceof PushItem)) {
GraphTargetItem prevExpr = stack.pop();
GraphTargetItem leftSide = expr;
GraphTargetItem rightSide = ((PushItem) onTrue.get(1)).value;
if (leftSide instanceof DuplicateItem) {
isIf = false;
stack.push(new OrItem(null, prevExpr, rightSide));
} else if (leftSide.invert(null) instanceof DuplicateItem) {
isIf = false;
stack.push(new AndItem(null, prevExpr, rightSide));
} else if (prevExpr instanceof FalseItem) {
isIf = false;
stack.push(new OrItem(null, leftSide, rightSide));
} else if (prevExpr instanceof TrueItem) {
isIf = false;
stack.push(new AndItem(null, leftSide, rightSide));
} else {
currentRet.add(new IfItem(null, expr, onTrue, onFalse));
//:-(
}
} else {
printGraph(visited, localData, stack, allParts, part, next, stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1);
}
if (isIf) {
makeAllCommands(currentRet, stack);
IfItem b = new IfItem(null, expr.invert(null), onTrue, onFalse);
currentRet.add(b);
if (processSubBlk(b, null)) {
stack.push(new PopItem(null));
}
}
}
//currentRet.addAll(out2);
if (next != null) {
printGraph(visited, localData, stack, allParts, part, next, stopPart, loops, currentRet, staticOperation, path, recursionLevel + 1);
//currentRet.addAll();
}
}
@@ -1751,7 +1667,7 @@ public class Graph {
stopContPart.add(currentLoop.loopContinue);
GraphPart precoBackup = currentLoop.loopPreContinue;
currentLoop.loopPreContinue = null;
loopItem.commands.addAll(printGraph(visited, localData, new TranslateStack(), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1));
loopItem.commands.addAll(printGraph(visited, localData, new TranslateStack(path), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1));
}
}
@@ -1788,7 +1704,7 @@ public class Graph {
GraphTargetItem expr = ifi.expression;
if (inverted) {
if (expr instanceof LogicalOpItem) {
expr = ((LogicalOpItem) expr).invert();
expr = ((LogicalOpItem) expr).invert(null);
} else {
expr = new NotItem(null, expr);
}
@@ -1808,7 +1724,7 @@ public class Graph {
currentLoop.loopPreContinue = null;
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
stopPart2.add(currentLoop.loopContinue);
finalComm = printGraph(visited, localData, new TranslateStack(), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
finalComm = printGraph(visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
currentLoop.loopPreContinue = backup;
checkContinueAtTheEnd(finalComm, currentLoop);
}
@@ -1848,11 +1764,7 @@ public class Graph {
List<GraphTargetItem> exprList = new ArrayList<>();
GraphTargetItem expr = ifi.expression;
if (inverted) {
if (expr instanceof LogicalOpItem) {
expr = ((LogicalOpItem) expr).invert();
} else {
expr = new NotItem(null, expr);
}
expr = expr.invert(null);
}
checkContinueAtTheEnd(bodyBranch, currentLoop);
@@ -1889,7 +1801,7 @@ public class Graph {
currentLoop.loopPreContinue = null;
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
stopPart2.add(currentLoop.loopContinue);
List<GraphTargetItem> finalComm = printGraph(visited, localData, new TranslateStack(), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
List<GraphTargetItem> finalComm = printGraph(visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
currentLoop.loopPreContinue = backup;
checkContinueAtTheEnd(finalComm, currentLoop);
@@ -1914,11 +1826,7 @@ public class Graph {
List<GraphTargetItem> exprList = new ArrayList<>(finalComm);
GraphTargetItem expr = ifi.expression;
if (invert) {
if (expr instanceof LogicalOpItem) {
expr = ((LogicalOpItem) expr).invert();
} else {
expr = new NotItem(null, expr);
}
expr = expr.invert(null);
}
exprList.add(expr);
ret.add(index, li = new DoWhileItem(null, currentLoop, loopItem.commands, exprList));
@@ -1943,7 +1851,7 @@ public class Graph {
}
if (currentLoop.loopBreak != null) {
ret.addAll(printGraph(visited, localData, stack, allParts, part, currentLoop.loopBreak, stopPart, loops, null, staticOperation, path, recursionLevel + 1));
ret.addAll(printGraph(visited, localData, sPreLoop, allParts, part, currentLoop.loopBreak, stopPart, loops, null, staticOperation, path, recursionLevel + 1));
}
}
@@ -2157,4 +2065,35 @@ public class Graph {
} while (part != null);
return ret;
}
protected static void makeAllStack(List<GraphTargetItem> commands, TranslateStack stack) {
int pcnt = 0;
for (int i = commands.size() - 1; i >= 0; i--) {
if (commands.get(i) instanceof PushItem) {
pcnt++;
} else {
break;
}
}
for (int i = commands.size() - pcnt; i < commands.size(); i++) {
stack.push(commands.remove(i).value);
i--;
}
}
protected static void makeAllCommands(List<GraphTargetItem> commands, TranslateStack stack) {
int clen = commands.size();
BreakItem br = null;
if (!commands.isEmpty()) {
if (commands.get(commands.size() - 1) instanceof BreakItem) {
clen--;
}
}
while (stack.size() > 0) {
GraphTargetItem p = stack.pop();
if (!(p instanceof PopItem)) {
commands.add(clen, new PushItem(p));
}
}
}
}