new for loop continues detection

This commit is contained in:
Jindra Petřík
2021-01-29 07:32:21 +01:00
parent f8f7d00981
commit bfe0fce888
14 changed files with 250 additions and 128 deletions

View File

@@ -391,69 +391,6 @@ public class Graph {
}
}
}
List<Set<GraphPart>> reachable = new ArrayList<>();
for (GraphPart p : parts) {
LinkedHashSet<GraphPart> r1 = new LinkedHashSet<>();
getReachableParts(localData, p, r1, loops, gotoParts);
Set<GraphPart> r2 = new LinkedHashSet<>();
r2.add(p);
r2.addAll(r1);
reachable.add(r2);
}
///List<GraphPart> first = reachable.get(0);
int commonLevel;
Map<GraphPart, Integer> levelMap = new HashMap<>();
for (Set<GraphPart> first : reachable) {
int maxclevel = 0;
Set<GraphPart> visited = new HashSet<>();
for (GraphPart p : first) {
if (loopContinues.contains(p)) {
break;
}
if (visited.contains(p)) {
continue;
}
visited.add(p);
boolean common = true;
commonLevel = 1;
for (Set<GraphPart> r : reachable) {
if (r == first) {
continue;
}
if (r.contains(p)) {
commonLevel++;
}
}
if (commonLevel <= maxclevel) {
continue;
}
maxclevel = commonLevel;
if (levelMap.containsKey(p)) {
if (levelMap.get(p) > commonLevel) {
commonLevel = levelMap.get(p);
}
}
levelMap.put(p, commonLevel);
if (common) {
//return p;
}
}
}
for (int i = reachable.size() - 1; i >= 2; i--) {
for (GraphPart p : levelMap.keySet()) {
if (levelMap.get(p) == i) {
return p;
}
}
}
for (GraphPart p : levelMap.keySet()) {
if (levelMap.get(p) == parts.size()) {
return p;
}
}
return null;
/*
s
List<Set<GraphPart>> reachable = new ArrayList<>();
Set<GraphPart> allReachable = new LinkedHashSet<>();
for (GraphPart p : parts) {
@@ -494,7 +431,6 @@ public class Graph {
//System.err.println("maxclevelpart = " + maxCommonLevelPart);
return maxCommonLevelPart;
*/
}
public GraphPart getNextNoJump(GraphPart part, BaseLocalData localData) {
@@ -1449,6 +1385,19 @@ public class Graph {
}
}
if (i < list.size() - 1) {
if (list.get(i + 1) instanceof ContinueItem) {
if ((!onTrue.isEmpty()) && (onFalse.isEmpty())) {
if (onTrue.get(onTrue.size() - 1) instanceof ContinueItem) {
if (((ContinueItem) onTrue.get(onTrue.size() - 1)).loopId == ((ContinueItem) list.get(i + 1)).loopId) {
onTrue.remove(onTrue.size() - 1);
}
}
}
}
}
if ((!onTrue.isEmpty()) && (!onFalse.isEmpty())) {
GraphTargetItem last = onTrue.get(onTrue.size() - 1);
if ((last instanceof ExitItem) || (last instanceof ContinueItem) || (last instanceof BreakItem)) {
@@ -2163,6 +2112,23 @@ public class Graph {
}
}
private void getContinuesCommands(List<GraphTargetItem> commands, List<List<GraphTargetItem>> result, long loopId) {
for (GraphTargetItem ti : commands) {
if (ti instanceof ContinueItem) {
ContinueItem ci = (ContinueItem) ti;
if (ci.loopId == loopId) {
result.add(commands);
}
}
if (ti instanceof Block) {
Block bl = (Block) ti;
for (List<GraphTargetItem> subCommands : bl.getSubs()) {
getContinuesCommands(subCommands, result, loopId);
}
}
}
}
protected List<GraphTargetItem> printGraph(List<GotoItem> foundGotos, List<GraphPartEdge> gotoTargets, Map<GraphPart, List<GraphTargetItem>> partCodes, Map<GraphPart, Integer> partCodePos, Set<GraphPart> visited, BaseLocalData localData, TranslateStack stack, Set<GraphPart> allParts, GraphPart parent, GraphPart part, List<GraphPart> stopPart, List<Loop> loops, List<GraphTargetItem> ret, int staticOperation, String path, int recursionLevel) throws InterruptedException {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
@@ -2715,6 +2681,7 @@ public class Graph {
GraphPart precoBackup = currentLoop.loopPreContinue;
currentLoop.loopPreContinue = null;
loopItem.commands.addAll(printGraph(foundGotos, gotoTargets, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, precoBackup, stopContPart, loops, null, staticOperation, path, recursionLevel + 1));
checkContinueAtTheEnd(loopItem.commands, currentLoop);
}
}
@@ -2778,9 +2745,26 @@ public class Graph {
currentLoop.loopPreContinue = null;
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
stopPart2.add(currentLoop.loopContinue);
finalComm = printGraph(foundGotos, gotoTargets, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
List<GraphTargetItem> precoCommands = printGraph(foundGotos, gotoTargets, partCodes, partCodePos, visited, localData, new TranslateStack(path), allParts, null, backup, stopPart2, loops, null, staticOperation, path, recursionLevel + 1);
currentLoop.loopPreContinue = backup;
checkContinueAtTheEnd(finalComm, currentLoop);
checkContinueAtTheEnd(precoCommands, currentLoop);
List<List<GraphTargetItem>> continueCommands = new ArrayList<>();
getContinuesCommands(commands, continueCommands, currentLoop.id);
if (continueCommands.isEmpty()) {
commands.addAll(precoCommands);
precoCommands = 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, precoCommands);
precoCommands = new ArrayList<>();
}
}
finalComm.addAll(precoCommands);
}
if (currentLoop.precontinueCommands != null) {
finalComm.addAll(currentLoop.precontinueCommands);

View File

@@ -28,10 +28,18 @@ import java.util.Map;
import java.util.Set;
/**
*
* Detects "precontinues" in Graph. A precontinue is target of continue
* statement in a for loop. For loop in this case has single backedge.
* Precontinue is predeccessor of loops backedge. Precontinue can have branches
* in it (and in some special cases like xml .() operator a while too). This
* class tries to simplify graph up to the level that precontinue is a single
* node.
*
* @author JPEXS
*/
public class GraphPrecontinueDetector {
public void detectPrecontinues(List<GraphPart> heads, Set<GraphPart> allParts, List<Loop> loops) {
boolean isSomethingTodo = false;
for (Loop el : loops) {
@@ -106,7 +114,12 @@ public class GraphPrecontinueDetector {
//printGraph(headNodes);
}
private void printGraph(List<Node> headNodes) {
/**
* Converts node graph to graphviz for easily display.
*
* @param headNodes
*/
public void printGraph(List<Node> headNodes) {
Set<Node> allNodes = new LinkedHashSet<>();
for (Node headNode : headNodes) {
populateNodes(headNode, allNodes);
@@ -196,7 +209,7 @@ public class GraphPrecontinueDetector {
whileNode.graphPart = node.graphPart;
whileNode.body = bodyNode;
whileNode.body.removeFromGraph();
whileNode.prev = new NonNullList<>(node.prev);
whileNode.prev = new ArrayList<>(node.prev);
node.replacePrevs(whileNode);
node.replaceNexts(whileNode);
whileNode.next.add(breakNode);
@@ -204,7 +217,8 @@ public class GraphPrecontinueDetector {
numWhile.setVal(numWhile.getVal() + 1);
}
for (Node n : result.next) {
List<Node> nexts = new ArrayList<>(result.next);
for (Node n : nexts) {
handleWhile(n, visited, numWhile);
}
return result;
@@ -231,8 +245,8 @@ public class GraphPrecontinueDetector {
JoinedNode joinedNode = new JoinedNode();
joinedNode.graphPart = node.graphPart;
joinedNode.nodes = nodeList;
joinedNode.next = new NonNullList<>(currentNode.next);
joinedNode.prev = new NonNullList<>(node.prev);
joinedNode.next = new ArrayList<>(currentNode.next);
joinedNode.prev = new ArrayList<>(node.prev);
node.replacePrevs(joinedNode);
currentNode.replaceNexts(joinedNode);
for (Node n : nodeList) {
@@ -243,7 +257,8 @@ public class GraphPrecontinueDetector {
numJoined.setVal(numJoined.getVal() + 1);
}
for (Node n : result.next) {
List<Node> nexts = new ArrayList<>(result.next);
for (Node n : nexts) {
joinNodes(n, visited, numJoined);
}
@@ -270,7 +285,7 @@ public class GraphPrecontinueDetector {
ifNode.onTrue.removeFromGraph();
ifNode.onFalse = null;
ifNode.graphPart = node.graphPart;
ifNode.prev = new NonNullList<>(node.prev);
ifNode.prev = new ArrayList<>(node.prev);
node.replacePrevs(ifNode);
Node after = node.next.get(1);
node.removeFromGraph();
@@ -300,7 +315,7 @@ public class GraphPrecontinueDetector {
ifNode.onFalse.parentNode = ifNode;
ifNode.onFalse.removeFromGraph();
ifNode.graphPart = node.graphPart;
ifNode.prev = new NonNullList<>(node.prev);
ifNode.prev = new ArrayList<>(node.prev);
node.replacePrevs(ifNode);
Node after = node.next.get(0);
node.removeFromGraph();
@@ -331,7 +346,7 @@ public class GraphPrecontinueDetector {
ifNode.onFalse.parentNode = ifNode;
ifNode.onFalse.removeFromGraph();
ifNode.graphPart = node.graphPart;
ifNode.prev = new NonNullList<>(node.prev);
ifNode.prev = new ArrayList<>(node.prev);
node.replacePrevs(ifNode);
Node after = node.next.get(0).next.get(0);
node.removeFromGraph();

View File

@@ -1,3 +1,19 @@
/*
* Copyright (C) 2010-2018 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.graph.precontinues;
/**

View File

@@ -1,3 +1,19 @@
/*
* Copyright (C) 2010-2018 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.graph.precontinues;
import java.util.ArrayList;

View File

@@ -26,8 +26,8 @@ import java.util.List;
* @author JPEXS
*/
public class Node {
public List<Node> next = new NonNullList<>();
public List<Node> prev = new NonNullList<Node>();
public List<Node> next = new ArrayList<>();
public List<Node> prev = new ArrayList<Node>();
public GraphPart graphPart;
private static int CURRENT_ID = 0;
private int id;

View File

@@ -1,39 +0,0 @@
package com.jpexs.decompiler.graph.precontinues;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
*
* @author JPEXS
*/
class NonNullList<T> extends ArrayList<T> {
public NonNullList(Collection<? extends T> col) {
super(col);
}
public NonNullList() {
}
@Override
public boolean add(T item) {
if (item == null) {
throw new NullPointerException("The collection does not support null values");
} else {
return super.add(item);
}
}
@Override
public boolean addAll(Collection<? extends T> items) {
if (items.contains(null)) {
throw new NullPointerException("The collection does not support null values");
} else {
return super.addAll(items);
}
}
}

View File

@@ -2008,8 +2008,6 @@ public class ActionScript2Test extends ActionScript2TestBase {
+ "case \"C\":\r\n"
+ "trace(\"Ret 5\");\r\n"
+ "return 5;\r\n"
+ "default:\r\n"
+ "continue;\r\n"
+ "}\r\n"
+ "}\r\n"
+ "trace(\"Final\");\r\n"

View File

@@ -251,10 +251,8 @@ public class ActionScript3Test extends ActionScriptTestBase {
+ "for(d = 0; d < 25; d++)\r\n"
+ "{\r\n"
+ "e = 0;\r\n"
+ "if(e >= 50)\r\n"
+ "if(e < 50)\r\n"
+ "{\r\n"
+ "continue;\r\n"
+ "}\r\n"
+ "if(e == 9)\r\n"
+ "{\r\n"
+ "break;\r\n"
@@ -269,6 +267,7 @@ public class ActionScript3Test extends ActionScriptTestBase {
+ "}\r\n"
+ "break loop1;\r\n"
+ "}\r\n"
+ "}\r\n"
+ "trace(\"hello\");\r\n"
+ "}\r\n",
false);
@@ -1658,6 +1657,27 @@ public class ActionScript3Test extends ActionScriptTestBase {
false);
}
@Test
public void testAssembledSwitch() {
decompileMethod("assembled", "testSwitch", "switch(int(somevar))\r\n"
+ "{\r\n"
+ "case 0:\r\n"
+ "var _loc2_:String = \"X\";\r\n"
+ "return;\r\n"
+ "break;\r\n"
+ "case 1:\r\n"
+ "_loc2_ = \"A\";\r\n"
+ "break;\r\n"
+ "case 3:\r\n"
+ "_loc2_ = \"B\";\r\n"
+ "break;\r\n"
+ "case 4:\r\n"
+ "_loc2_ = \"C\";\r\n"
+ "}\r\n"
+ "_loc2_ = \"after\";\r\n",
false);
}
@Test
public void testAssembledSwitchDefault() {
decompileMethod("assembled", "testSwitchDefault", "switch(5)\r\n"

View File

@@ -12,7 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* License along with this library.
*/
package com.jpexs.decompiler.flash.generators;
import com.jpexs.decompiler.flash.SWF;
@@ -90,12 +91,10 @@ public class AS2Generator {
s.append("}");
doa = null;
}
}
/*try (PrintWriter pw = new PrintWriter("as2_teststub.java")) {
pw.println(s.toString());
}*/
try (FileOutputStream fos = new FileOutputStream("as2_teststub.java")) {
fos.write(Utf8Helper.getBytes(s.toString()));
}
try (FileOutputStream fos = new FileOutputStream("as2_teststub.java")) {
fos.write(Utf8Helper.getBytes(s.toString()));
}
System.exit(0);
}
}

View File

@@ -15,5 +15,6 @@ program
#include "tests/TestIncrement3.script.asasm"
#include "tests/TestDup.script.asasm"
#include "tests/TestSwitchDefault.script.asasm"
#include "tests/TestSwitch.script.asasm"
; place to add next
end ; program

View File

@@ -0,0 +1,83 @@
class
refid "tests:TestSwitch"
instance QName(PackageNamespace("tests"), "TestSwitch")
extends QName(PackageNamespace(""), "Object")
flag SEALED
flag PROTECTEDNS
protectedns ProtectedNamespace("tests:TestSwitch")
iinit
refid "tests:TestSwitch/instance/init"
body
maxstack 1
localcount 1
initscopedepth 4
maxscopedepth 5
code
getlocal0
pushscope
getlocal0
constructsuper 0
returnvoid
end ; code
end ; body
end ; method
trait method QName(PackageNamespace(""), "run")
method
refid "tests:TestSwitch/instance/run"
returns QName(PackageNamespace(""), "void")
body
maxstack 2
localcount 4
initscopedepth 4
maxscopedepth 5
code
getlocal0
pushscope
getlex QName(PrivateNamespace("somens"),"somevar")
convert_i
lookupswitch ofs005e, [ofs002d, ofs0038, ofs005e, ofs0050, ofs0059]
ofs002d:
pushstring "X"
setlocal2
returnvoid
ofs0038:
pushstring "A"
coerce_s
setlocal2
jump ofs005e
ofs0050:
pushstring "B"
coerce_s
setlocal2
jump ofs005e
ofs0059:
pushstring "C"
coerce_s
setlocal2
ofs005e:
pushstring "after"
setlocal2
returnvoid
end ; code
end ; body
end ; method
end ; trait
end ; instance
cinit
refid "tests:TestSwitch/class/init"
body
maxstack 1
localcount 1
initscopedepth 3
maxscopedepth 4
code
getlocal0
pushscope
returnvoid
end ; code
end ; body
end ; method
end ; class

View File

@@ -0,0 +1,29 @@
script
sinit
refid "tests:TestSwitch/init"
body
maxstack 2
localcount 1
initscopedepth 1
maxscopedepth 3
code
getlocal0
pushscope
findpropstrict Multiname("TestSwitch", [PackageNamespace("tests")])
getlex QName(PackageNamespace(""), "Object")
pushscope
getlex Multiname("Object", [PrivateNamespace(null, "tests:TestSwitch"), PackageNamespace(""), PackageNamespace("tests"), PackageInternalNs("tests"), Namespace("http://adobe.com/AS3/2006/builtin")])
newclass "tests:TestSwitch"
popscope
initproperty QName(PackageNamespace("tests"), "TestSwitch")
returnvoid
end ; code
end ; body
end ; method
trait class QName(PackageNamespace("tests"), "TestSwitch")
#include "TestSwitch.class.asasm"
end ; trait
end ; script

Binary file not shown.