mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-30 09:37:19 +00:00
new for loop continues detection WIP
This commit is contained in:
@@ -54,6 +54,7 @@ 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.decompiler.graph.precontinues.GraphPrecontinueDetector;
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -577,11 +578,14 @@ public class Graph {
|
||||
|
||||
//TODO: Make getPrecontinues faster
|
||||
getBackEdges(localData, loops, new ArrayList<>());
|
||||
|
||||
new GraphPrecontinueDetector().detectPrecontinues(heads, allParts, loops);
|
||||
|
||||
//getPrecontinues(path, localData, null, heads.get(0), allParts, loops, null);
|
||||
//getPrecontinues2(path, localData, null, heads.get(0), allParts, loops, null);
|
||||
List<GraphPartEdge> gotoTargets = new ArrayList<>();
|
||||
|
||||
findGotoTargets(localData, path, heads.get(0), allParts, loops, gotoTargets);
|
||||
//findGotoTargets(localData, path, heads.get(0), allParts, loops, gotoTargets);
|
||||
|
||||
/*System.err.println("<loopspre>");
|
||||
for (Loop el : loops) {
|
||||
@@ -2769,7 +2773,7 @@ public class Graph {
|
||||
List<GraphTargetItem> finalComm = new ArrayList<>();
|
||||
|
||||
//findGotoTargets - comment this out:
|
||||
/*if (currentLoop.loopPreContinue != null) {
|
||||
if (currentLoop.loopPreContinue != null) {
|
||||
GraphPart backup = currentLoop.loopPreContinue;
|
||||
currentLoop.loopPreContinue = null;
|
||||
List<GraphPart> stopPart2 = new ArrayList<>(stopPart);
|
||||
@@ -2777,7 +2781,7 @@ public class Graph {
|
||||
finalComm = 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);
|
||||
}*/
|
||||
}
|
||||
if (currentLoop.precontinueCommands != null) {
|
||||
finalComm.addAll(currentLoop.precontinueCommands);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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 com.jpexs.decompiler.graph.GraphPart;
|
||||
import com.jpexs.decompiler.graph.Loop;
|
||||
import com.jpexs.helpers.Reference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class GraphPrecontinueDetector {
|
||||
public void detectPrecontinues(List<GraphPart> heads, Set<GraphPart> allParts, List<Loop> loops) {
|
||||
boolean isSomethingTodo = false;
|
||||
for (Loop el : loops) {
|
||||
if (el.backEdges.size() == 1) {
|
||||
isSomethingTodo = true;
|
||||
}
|
||||
}
|
||||
if (!isSomethingTodo) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<GraphPart, Node> partToNode = new HashMap<>();
|
||||
for (GraphPart part : allParts) {
|
||||
Node node = new Node();
|
||||
node.graphPart = part;
|
||||
partToNode.put(part, node);
|
||||
}
|
||||
for (GraphPart part : allParts) {
|
||||
Node node = partToNode.get(part);
|
||||
for (GraphPart prev : part.refs) {
|
||||
if (prev.start < 0) {
|
||||
continue;
|
||||
}
|
||||
Node prevNode = partToNode.get(prev);
|
||||
node.prev.add(prevNode);
|
||||
}
|
||||
for (GraphPart next : part.nextParts) {
|
||||
Node nextNode = partToNode.get(next);
|
||||
node.next.add(nextNode);
|
||||
}
|
||||
}
|
||||
List<Node> headNodes = new ArrayList<>();
|
||||
for (GraphPart head : heads) {
|
||||
headNodes.add(partToNode.get(head));
|
||||
}
|
||||
|
||||
|
||||
boolean changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
if (joinNodes(headNodes)) {
|
||||
changed = true;
|
||||
}
|
||||
if (checkIfs(headNodes)) {
|
||||
changed = true;
|
||||
}
|
||||
if (handleWhile(headNodes)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Loop el : loops) {
|
||||
if (el.backEdges.size() == 1) {
|
||||
//System.err.println("loop " + el.loopContinue);
|
||||
GraphPart backEdgePart = el.backEdges.iterator().next();
|
||||
Node node = partToNode.get(backEdgePart);
|
||||
//System.err.println("backedge:" + backEdgePart);
|
||||
boolean wholeLoop = false;
|
||||
while (node.parentNode != null) {
|
||||
node = node.parentNode;
|
||||
//System.err.println("- parent " + node.graphPart);
|
||||
if (node.graphPart.equals(el.loopContinue)) {
|
||||
wholeLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!wholeLoop) {
|
||||
el.loopPreContinue = node.graphPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
//printGraph(headNodes);
|
||||
}
|
||||
|
||||
private void printGraph(List<Node> headNodes) {
|
||||
Set<Node> allNodes = new LinkedHashSet<>();
|
||||
for (Node headNode : headNodes) {
|
||||
populateNodes(headNode, allNodes);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("digraph mygraph {\r\n");
|
||||
for (Node node : allNodes) {
|
||||
sb.append("node" + node.getId() + "[label=\"" + node.toString() + "\"];\r\n");
|
||||
for (Node n : node.next) {
|
||||
sb.append("node" + node.getId() + "->node" + n.getId() + ";\r\n");
|
||||
}
|
||||
}
|
||||
sb.append("}\r\n");
|
||||
System.err.println(sb.toString());
|
||||
}
|
||||
|
||||
private void populateNodes(Node node, Set<Node> populated) {
|
||||
if (populated.contains(node)) {
|
||||
return;
|
||||
}
|
||||
populated.add(node);
|
||||
for (Node n : node.next) {
|
||||
populateNodes(n, populated);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkIfs(List<Node> headNodes) {
|
||||
Set<Node> visited = new HashSet<>();
|
||||
Reference<Integer> numChanged = new Reference<>(0);
|
||||
for (int h = 0; h < headNodes.size(); h++) {
|
||||
Node newHeadNode;
|
||||
newHeadNode = checkIfs(headNodes.get(h), visited, numChanged);
|
||||
headNodes.set(h, newHeadNode);
|
||||
}
|
||||
return numChanged.getVal() > 0;
|
||||
}
|
||||
|
||||
private boolean joinNodes(List<Node> headNodes) {
|
||||
Set<Node> visited = new HashSet<>();
|
||||
Reference<Integer> numChanged = new Reference<>(0);
|
||||
for (int h = 0; h < headNodes.size(); h++) {
|
||||
Node newHeadNode;
|
||||
newHeadNode = joinNodes(headNodes.get(h), visited, numChanged);
|
||||
headNodes.set(h, newHeadNode);
|
||||
}
|
||||
return numChanged.getVal() > 0;
|
||||
}
|
||||
|
||||
private boolean handleWhile(List<Node> headNodes) {
|
||||
Set<Node> visited = new HashSet<>();
|
||||
Reference<Integer> numChanged = new Reference<>(0);
|
||||
for (int h = 0; h < headNodes.size(); h++) {
|
||||
Node newHeadNode;
|
||||
newHeadNode = handleWhile(headNodes.get(h), visited, numChanged);
|
||||
headNodes.set(h, newHeadNode);
|
||||
}
|
||||
return numChanged.getVal() > 0;
|
||||
}
|
||||
|
||||
private Node handleWhile(Node node, Set<Node> visited, Reference<Integer> numWhile) {
|
||||
if (visited.contains(node)) {
|
||||
return node;
|
||||
}
|
||||
visited.add(node);
|
||||
|
||||
Node result = node;
|
||||
|
||||
Node bodyNode = null;
|
||||
Node breakNode = null;
|
||||
if (node.next.size() == 2
|
||||
&& node.next.get(0).next.size() == 1
|
||||
&& node.next.get(0).next.get(0) == node) {
|
||||
breakNode = node.next.get(1);
|
||||
bodyNode = node.next.get(0);
|
||||
} else if (node.next.size() == 2
|
||||
&& node.next.get(1).next.size() == 1
|
||||
&& node.next.get(1).next.get(0) == node) {
|
||||
breakNode = node.next.get(0);
|
||||
bodyNode = node.next.get(1);
|
||||
}
|
||||
|
||||
if (bodyNode != null) {
|
||||
|
||||
WhileNode whileNode = new WhileNode();
|
||||
bodyNode.parentNode = whileNode;
|
||||
whileNode.graphPart = node.graphPart;
|
||||
whileNode.body = bodyNode;
|
||||
whileNode.body.removeFromGraph();
|
||||
whileNode.prev = new NonNullList<>(node.prev);
|
||||
node.replacePrevs(whileNode);
|
||||
node.replaceNexts(whileNode);
|
||||
whileNode.next.add(breakNode);
|
||||
result = whileNode;
|
||||
numWhile.setVal(numWhile.getVal() + 1);
|
||||
}
|
||||
|
||||
for (Node n : result.next) {
|
||||
handleWhile(n, visited, numWhile);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node joinNodes(Node node, Set<Node> visited, Reference<Integer> numJoined) {
|
||||
if (visited.contains(node)) {
|
||||
return node;
|
||||
}
|
||||
visited.add(node);
|
||||
Node currentNode = node;
|
||||
List<Node> nodeList = new ArrayList<>();
|
||||
nodeList.add(currentNode);
|
||||
while (currentNode.next.size() == 1
|
||||
&& currentNode.next.get(0).prev.size() == 1
|
||||
&& !visited.contains(currentNode.next.get(0))) {
|
||||
currentNode = currentNode.next.get(0);
|
||||
visited.add(currentNode);
|
||||
nodeList.add(currentNode);
|
||||
}
|
||||
|
||||
Node result = node;
|
||||
if (nodeList.size() > 1) {
|
||||
JoinedNode joinedNode = new JoinedNode();
|
||||
joinedNode.graphPart = node.graphPart;
|
||||
joinedNode.nodes = nodeList;
|
||||
joinedNode.next = new NonNullList<>(currentNode.next);
|
||||
joinedNode.prev = new NonNullList<>(node.prev);
|
||||
node.replacePrevs(joinedNode);
|
||||
currentNode.replaceNexts(joinedNode);
|
||||
for (Node n : nodeList) {
|
||||
n.parentNode = joinedNode;
|
||||
n.removeFromGraph();
|
||||
}
|
||||
result = joinedNode;
|
||||
numJoined.setVal(numJoined.getVal() + 1);
|
||||
}
|
||||
|
||||
for (Node n : result.next) {
|
||||
joinNodes(n, visited, numJoined);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Node checkIfs(Node node, Set<Node> visited, Reference<Integer> numIfs) {
|
||||
if (visited.contains(node)) {
|
||||
return node;
|
||||
}
|
||||
visited.add(node);
|
||||
/*
|
||||
if(a)
|
||||
{
|
||||
onTrue
|
||||
}
|
||||
*/
|
||||
if (node.next.size() == 2
|
||||
&& node.next.get(0).next.size() == 1
|
||||
&& node.next.get(0).next.get(0) == node.next.get(1)) {
|
||||
IfNode ifNode = new IfNode();
|
||||
ifNode.onTrue = node.next.get(0);
|
||||
ifNode.onTrue.parentNode = ifNode;
|
||||
ifNode.onTrue.removeFromGraph();
|
||||
ifNode.onFalse = null;
|
||||
ifNode.graphPart = node.graphPart;
|
||||
ifNode.prev = new NonNullList<>(node.prev);
|
||||
node.replacePrevs(ifNode);
|
||||
Node after = node.next.get(1);
|
||||
node.removeFromGraph();
|
||||
ifNode.next.add(after);
|
||||
after.prev.add(ifNode);
|
||||
numIfs.setVal(numIfs.getVal() + 1);
|
||||
checkIfs(after, visited, numIfs);
|
||||
return ifNode;
|
||||
}
|
||||
|
||||
/*
|
||||
if(a)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
onFalse
|
||||
}
|
||||
*/
|
||||
if (node.next.size() == 2
|
||||
&& node.next.get(1).next.size() == 1
|
||||
&& node.next.get(1).next.get(0) == node.next.get(0)) {
|
||||
IfNode ifNode = new IfNode();
|
||||
ifNode.onTrue = null;
|
||||
ifNode.onFalse = node.next.get(1);
|
||||
ifNode.onFalse.parentNode = ifNode;
|
||||
ifNode.onFalse.removeFromGraph();
|
||||
ifNode.graphPart = node.graphPart;
|
||||
ifNode.prev = new NonNullList<>(node.prev);
|
||||
node.replacePrevs(ifNode);
|
||||
Node after = node.next.get(0);
|
||||
node.removeFromGraph();
|
||||
ifNode.next.add(after);
|
||||
after.prev.add(ifNode);
|
||||
numIfs.setVal(numIfs.getVal() + 1);
|
||||
checkIfs(after, visited, numIfs);
|
||||
return ifNode;
|
||||
}
|
||||
/*
|
||||
if(a)
|
||||
{
|
||||
onTrue
|
||||
}
|
||||
else
|
||||
{
|
||||
onFalse
|
||||
}
|
||||
*/
|
||||
if (node.next.size() == 2
|
||||
&& node.next.get(0).next.size() == 1
|
||||
&& node.next.get(1).next.size() == 1
|
||||
&& node.next.get(0).next.get(0) == node.next.get(1).next.get(0)) {
|
||||
IfNode ifNode = new IfNode();
|
||||
ifNode.onTrue = node.next.get(0);
|
||||
ifNode.onTrue.parentNode = ifNode;
|
||||
ifNode.onFalse = node.next.get(1);
|
||||
ifNode.onFalse.parentNode = ifNode;
|
||||
ifNode.onFalse.removeFromGraph();
|
||||
ifNode.graphPart = node.graphPart;
|
||||
ifNode.prev = new NonNullList<>(node.prev);
|
||||
node.replacePrevs(ifNode);
|
||||
Node after = node.next.get(0).next.get(0);
|
||||
node.removeFromGraph();
|
||||
ifNode.next.add(after);
|
||||
after.prev.add(ifNode);
|
||||
numIfs.setVal(numIfs.getVal() + 1);
|
||||
checkIfs(after, visited, numIfs);
|
||||
return ifNode;
|
||||
}
|
||||
|
||||
for (Node n : node.next) {
|
||||
checkIfs(n, visited, numIfs);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.jpexs.decompiler.graph.precontinues;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class IfNode extends Node {
|
||||
public Node onTrue;
|
||||
public Node onFalse;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "if" + super.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.jpexs.decompiler.graph.precontinues;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class JoinedNode extends Node {
|
||||
public List<Node> nodes = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "join" + super.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 com.jpexs.decompiler.graph.GraphPart;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Node {
|
||||
public List<Node> next = new NonNullList<>();
|
||||
public List<Node> prev = new NonNullList<Node>();
|
||||
public GraphPart graphPart;
|
||||
private static int CURRENT_ID = 0;
|
||||
private int id;
|
||||
|
||||
public Node parentNode;
|
||||
|
||||
public Node() {
|
||||
this.id = ++CURRENT_ID;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "node" + id + ":" + (graphPart == null ? "null" : graphPart.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 53 * hash + this.id;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Node other = (Node) obj;
|
||||
if (this.id != other.id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void replacePrevs(Node newNode) {
|
||||
for (Node p : this.prev) {
|
||||
for (int i = 0; i < p.next.size(); i++) {
|
||||
if (p.next.get(i) == this) {
|
||||
p.next.set(i, newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void replaceNexts(Node newNode) {
|
||||
for (Node n : this.next) {
|
||||
for (int i = 0; i < n.prev.size(); i++) {
|
||||
if (n.prev.get(i) == this) {
|
||||
n.prev.set(i, newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFromGraph() {
|
||||
for (Node p : this.prev) {
|
||||
for (int i = p.next.size() - 1; i >= 0; i--) {
|
||||
if (p.next.get(i) == this) {
|
||||
p.next.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Node n : this.next) {
|
||||
for (int i = n.prev.size() - 1; i >= 0; i--) {
|
||||
if (n.prev.get(i) == this) {
|
||||
n.prev.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
next.clear();
|
||||
prev.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.jpexs.decompiler.graph.precontinues;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class WhileNode extends Node {
|
||||
public Node body;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "while" + super.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user