Fixed goto/for detection

This commit is contained in:
Jindra Petřík
2021-01-23 12:55:13 +01:00
parent c5f992b675
commit 3f2e075ee9
8 changed files with 220 additions and 30 deletions

View File

@@ -659,7 +659,7 @@ public class AVM2Graph extends Graph {
}
GraphPart breakPart = getMostCommonPart(localData, caseBodyParts, loops, new ArrayList<>());
removeEdgeToFromList(gotoTargets, breakPart);
//removeEdgeToFromList(gotoTargets, breakPart);
List<List<GraphTargetItem>> caseCommands = new ArrayList<>();
GraphPart next = breakPart;

View File

@@ -467,7 +467,7 @@ public class ActionGraph extends Graph {
}
GraphPart breakPart = getMostCommonPart(localData, caseBodyParts, loops, new ArrayList<>());
removeEdgeToFromList(gotoTargets, breakPart);
//removeEdgeToFromList(gotoTargets, breakPart);
List<List<GraphTargetItem>> caseCommands = new ArrayList<>();
GraphPart next = breakPart;
@@ -482,7 +482,7 @@ public class ActionGraph extends Graph {
for (int i = 0; i < caseValuesMap.size(); i++) {
GraphPart cur = caseBodyParts.get(i);
if (!caseBodies.contains(cur)) {
removeEdgeToFromList(gotoTargets, cur);
//removeEdgeToFromList(gotoTargets, cur);
caseBodies.add(cur);
}
valuesMapping.add(caseBodies.indexOf(cur));

View File

@@ -542,6 +542,9 @@ public class Graph {
System.err.println("</loopspre>");//*/
List<GotoItem> gotos = new ArrayList<>();
List<GraphTargetItem> ret = printGraph(gotos, gotoTargets, new HashMap<>(), new HashMap<>(), localData, stack, allParts, null, heads.get(0), null, loops, staticOperation, path);
processIfGotos(gotos, ret);
Map<String, Integer> usages = new HashMap<>();
Map<String, GotoItem> lastUsage = new HashMap<>();
for (GotoItem gi : gotos) {
@@ -552,10 +555,12 @@ public class Graph {
lastUsage.put(gi.labelName, gi);
}
for (String labelName : usages.keySet()) {
logger.fine("usage - " + labelName + ": " + usages.get(labelName));
if (usages.get(labelName) == 1) {
lastUsage.get(labelName).labelName = null;
}
}
expandGotos(ret);
processIfs(ret);
finalProcessStack(stack, ret, path);
@@ -735,6 +740,7 @@ public class Graph {
}
logger.fine("current branches: " + System.identityHashCode(branches) + ": " + pathToString(branches));
List<GraphPartEdge> foundBreakEdges = new ArrayList<>();
if (comparedPaths.size() > 1) {
logger.fine("not a single path - paths left: " + comparedPaths.size());
List<GraphPartDecision> prefix = getCommonPrefix(comparedPaths);
@@ -754,6 +760,7 @@ public class Graph {
logger.fine("branch already closed: " + partToClose);
logger.fine("probably break edge: " + edgeToClose);
if (ignoredBreakEdges.contains(edgeToClose)) {
foundBreakEdges.add(edgeToClose);
logger.fine("NOT a break edge, it is standard break");
continue;
}
@@ -788,6 +795,10 @@ public class Graph {
for (GraphPart r : p.refs) {
gotoTargets.add(new GraphPartEdge(r, p));
}
/*for (GraphPartEdge be : foundBreakEdges) {
GraphPartEdge re = findGotoRestoreOrigEdge(be);
gotoTargets.add(re);
}*/
}
}
@@ -849,6 +860,24 @@ public class Graph {
}
}
/*private GraphPartEdge findGotoRestoreOrigEdge(GraphPartEdge e) {
for (GraphPart r : e.to.refs) {
if (r.equals(e.from)) {
return e;
}
}
for (GraphPart r : e.to.refs) {
GraphPart rr = r;
while (isPartEmpty(rr) && rr.refs.size() == 1) {
rr = rr.refs.get(0);
}
if (rr.equals(e.from)) {
return new GraphPartEdge(r, e.to);
}
}
return null;
}*/
protected boolean isPartEmpty(GraphPart part) {
return false;
}
@@ -1278,6 +1307,58 @@ public class Graph {
}
}
/**
* if (xxx) { y ; goto a } else { z ; goto a }
*
* =>
*
* if (xxx) { y } else { z } goto a
*
*/
private void processIfGotos(List<GotoItem> allGotos, List<GraphTargetItem> list) {
for (int i = 0; i < list.size(); i++) {
GraphTargetItem item = list.get(i);
if (item instanceof Block) {
List<List<GraphTargetItem>> subs = ((Block) item).getSubs();
for (List<GraphTargetItem> sub : subs) {
processIfGotos(allGotos, sub);
}
}
if (item instanceof IfItem) {
IfItem ii = (IfItem) item;
if (!ii.onTrue.isEmpty() && !ii.onFalse.isEmpty()) {
if (ii.onTrue.get(ii.onTrue.size() - 1) instanceof GotoItem) {
if (ii.onFalse.get(ii.onFalse.size() - 1) instanceof GotoItem) {
GotoItem gotoOnTrue = (GotoItem) ii.onTrue.get(ii.onTrue.size() - 1);
GotoItem gotoOnFalse = (GotoItem) ii.onFalse.get(ii.onFalse.size() - 1);
if (gotoOnTrue.labelName.equals(gotoOnFalse.labelName)) {
String labelOnTrue = gotoOnTrue.labelName;
String labelOnFalse = gotoOnFalse.labelName;
if (labelOnTrue != null && labelOnFalse != null) {
if (labelOnTrue.equals(labelOnFalse)) {
GotoItem gotoMerged;
GotoItem gotoRemoved;
if (gotoOnTrue.targetCommands != null) {
gotoMerged = gotoOnTrue;
gotoRemoved = gotoOnFalse;
} else {
gotoMerged = gotoOnFalse;
gotoRemoved = gotoOnTrue;
}
ii.onTrue.remove(ii.onTrue.size() - 1);
ii.onFalse.remove(ii.onFalse.size() - 1);
list.add(i + 1, gotoMerged);
allGotos.remove(gotoRemoved);
}
}
}
}
}
}
}
}
}
private void processIfs(List<GraphTargetItem> list) {
for (int i = 0; i < list.size(); i++) {
GraphTargetItem item = list.get(i);
@@ -1297,25 +1378,6 @@ public class Graph {
IfItem ifi = (IfItem) item;
List<GraphTargetItem> onTrue = ifi.onTrue;
List<GraphTargetItem> onFalse = ifi.onFalse;
if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) {
if (onTrue.get(onTrue.size() - 1) instanceof GotoItem) {
if (onFalse.get(onFalse.size() - 1) instanceof GotoItem) {
GotoItem gotoOnTrue = (GotoItem) onTrue.get(onTrue.size() - 1);
GotoItem gotoOnFalse = (GotoItem) onFalse.get(onFalse.size() - 1);
String labelOnTrue = gotoOnTrue.labelName;
String labelOnFalse = gotoOnFalse.labelName;
if (labelOnTrue != null && labelOnFalse != null) {
if (labelOnTrue.equals(labelOnFalse)) {
GotoItem gotoMerged = gotoOnTrue.targetCommands != null ? gotoOnTrue : gotoOnFalse;
onTrue.remove(onTrue.size() - 1);
onFalse.remove(onFalse.size() - 1);
list.add(i + 1, gotoMerged);
}
}
}
}
}
if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) {
if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) {
if (onFalse.get(onFalse.size() - 1) instanceof ContinueItem) {
@@ -2195,7 +2257,7 @@ public class Graph {
gi.targetCommands.remove(cnt);
l.precontinueCommands = gi.targetCommands;
l.loopPreContinue = part;
removeEdgeToFromList(gotoTargets, part);
//removeEdgeToFromList(gotoTargets, part);
ret.add(cnt);
return ret;
}
@@ -2291,7 +2353,7 @@ public class Graph {
List<List<GraphTargetItem>> caseCommands = new ArrayList<>();
List<Integer> valueMappings = new ArrayList<>();
Loop swLoop = new Loop(loops.size(), null, next);
gotoTargets.remove(next);
//removeEdgeToFromList(gotoTargets, next);
swLoop.phase = 1;
loops.add(swLoop);
boolean first = false;
@@ -2383,7 +2445,7 @@ public class Graph {
pos = 0;
List<GraphTargetItem> nextCommands = new ArrayList<>();
for (int i = 1; i < part.nextParts.size(); i++) {
gotoTargets.remove(part.nextParts.get(i));
//gotoTargets.remove(new GraphPartEdge(next, part.nextParts.get(i)));
}
for (int i = 1; i < part.nextParts.size(); i++) {
GraphPart p = part.nextParts.get(i);
@@ -3100,5 +3162,9 @@ public class Graph {
edges.remove(i);
}
}
while (isPartEmpty(to) && !to.nextParts.isEmpty()) {
to = to.nextParts.get(0);
removeEdgeToFromList(edges, to);
}
}
}

View File

@@ -31,16 +31,11 @@ import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.graph.DottedChain;
import com.jpexs.decompiler.graph.ScopeStack;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
@@ -729,6 +724,60 @@ public class ActionScript3Test extends ActionScriptTestBase {
false);
}
@Test
public void testGotos6() {
decompileMethod("testGotos6", "var a:Boolean = true;\r\n"
+ "var s:String = \"a\";\r\n"
+ "if(a)\r\n"
+ "{\r\n"
+ "switch(s)\r\n"
+ "{\r\n"
+ "case \"a\":\r\n"
+ "trace(\"is A\");\r\n"
+ "break;\r\n"
+ "case \"b\":\r\n"
+ "trace(\"is B\");\r\n"
+ "case \"c\":\r\n"
+ "trace(\"is BC\");\r\n"
+ "}\r\n"
+ "}\r\n"
+ "else\r\n"
+ "{\r\n"
+ "trace(\"D\");\r\n"
+ "}\r\n"
+ "trace(\"finish\");\r\n",
false);
}
@Test
public void testGotos7() {
decompileMethod("testGotos7", "for(var i:int = 0; i < 10; i++)\r\n"
+ "{\r\n"
+ "switch(i)\r\n"
+ "{\r\n"
+ "case 0:\r\n"
+ "trace(\"zero\");\r\n"
+ "continue;\r\n"
+ "case 5:\r\n"
+ "trace(\"five\");\r\n"
+ "break;\r\n"
+ "case 10:\r\n"
+ "trace(\"ten\");\r\n"
+ "break;\r\n"
+ "case 1:\r\n"
+ "if(i == 7)\r\n"
+ "{\r\n"
+ "continue;\r\n"
+ "}\r\n"
+ "trace(\"one\");\r\n"
+ "default:\r\n"
+ "trace(\"def\");\r\n"
+ "}\r\n"
+ "trace(\"before loop end\");\r\n"
+ "}\r\n",
false);
}
@Test
public void testHello() {
decompileMethod("testHello", "trace(\"hello\");\r\n",

View File

@@ -41,6 +41,8 @@ package
TestGotos3;
TestGotos4;
TestGotos5;
TestGotos6;
TestGotos7;
TestHello;
TestIf;
TestIfElse;

View File

@@ -0,0 +1,35 @@
package tests
{
public class TestGotos6
{
public function run():void
{
var a:Boolean = true;
var s:String = "a";
if (a)
{
switch (s)
{
case "a":
trace("is A");
break;
case "b":
trace("is B");
case "c":
trace("is BC");
break;
}
}
else
{
trace("D");
}
trace("finish");
}
}
}

View File

@@ -0,0 +1,38 @@
package tests
{
public class TestGotos7
{
public function run():void
{
for (var i:int = 0; i < 10; i++)
{
switch (i)
{
case 0:
trace("zero");
continue;
case 5:
trace("five");
break;
case 10:
trace("ten");
break;
case 1:
if (i == 7)
{
continue;
}
trace("one");
default:
trace("def");
}
trace("before loop end");
}
}
}
}