Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java

666 lines
20 KiB

// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.*;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
import java.util.*;
import java.util.Map.Entry;
public class StackVarsProcessor {
public void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) {
Set<Integer> setReorderedIfs = new HashSet<>();
SSAUConstructorSparseEx ssau = null;
while (true) {
boolean found = false;
SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
ssa.splitVariables(root, mt);
SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null);
while (sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) {
found = true;
}
setVersionsToNull(root);
SequenceHelper.condenseSequences(root);
ssau = new SSAUConstructorSparseEx();
ssau.splitVariables(root, mt);
if (iterateStatements(root, ssau)) {
found = true;
}
setVersionsToNull(root);
if (!found) {
break;
}
}
// remove unused assignments
ssau = new SSAUConstructorSparseEx();
ssau.splitVariables(root, mt);
iterateStatements(root, ssau);
setVersionsToNull(root);
}
private static void setVersionsToNull(Statement stat) {
if (stat.getExprents() == null) {
for (Object obj : stat.getSequentialObjects()) {
if (obj instanceof Statement) {
setVersionsToNull((Statement)obj);
}
else if (obj instanceof Exprent) {
setExprentVersionsToNull((Exprent)obj);
}
}
}
else {
for (Exprent exprent : stat.getExprents()) {
setExprentVersionsToNull(exprent);
}
}
}
private static void setExprentVersionsToNull(Exprent exprent) {
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
for (Exprent expr : lst) {
if (expr.type == Exprent.EXPRENT_VAR) {
((VarExprent)expr).setVersion(0);
}
}
}
private boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) {
FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
DirectGraph dgraph = flatthelper.buildDirectGraph(root);
boolean res = false;
Set<DirectNode> setVisited = new HashSet<>();
LinkedList<DirectNode> stack = new LinkedList<>();
LinkedList<Map<VarVersionPair, Exprent>> stackMaps = new LinkedList<>();
stack.add(dgraph.first);
stackMaps.add(new HashMap<>());
while (!stack.isEmpty()) {
DirectNode nd = stack.removeFirst();
Map<VarVersionPair, Exprent> mapVarValues = stackMaps.removeFirst();
if (setVisited.contains(nd)) {
continue;
}
setVisited.add(nd);
List<List<Exprent>> lstLists = new ArrayList<>();
if (!nd.exprents.isEmpty()) {
lstLists.add(nd.exprents);
}
if (nd.succs.size() == 1) {
DirectNode ndsucc = nd.succs.get(0);
if (ndsucc.type == DirectNode.NODE_TAIL && !ndsucc.exprents.isEmpty()) {
lstLists.add(nd.succs.get(0).exprents);
nd = ndsucc;
}
}
for (int i = 0; i < lstLists.size(); i++) {
List<Exprent> lst = lstLists.get(i);
int index = 0;
while (index < lst.size()) {
Exprent next = null;
if (index == lst.size() - 1) {
if (i < lstLists.size() - 1) {
next = lstLists.get(i + 1).get(0);
}
}
else {
next = lst.get(index + 1);
}
int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa);
if (ret[0] >= 0) {
index = ret[0];
}
else {
index++;
}
res |= (ret[1] == 1);
}
}
for (DirectNode ndx : nd.succs) {
stack.add(ndx);
stackMaps.add(new HashMap<>(mapVarValues));
}
// make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty
// change loop type if necessary
if (nd.exprents.isEmpty() &&
(nd.type == DirectNode.NODE_INIT || nd.type == DirectNode.NODE_CONDITION || nd.type == DirectNode.NODE_INCREMENT)) {
nd.exprents.add(null);
if (nd.statement.type == Statement.TYPE_DO) {
DoStatement loop = (DoStatement)nd.statement;
if (loop.getLooptype() == DoStatement.LOOP_FOR &&
loop.getInitExprent() == null &&
loop.getIncExprent() == null) { // "downgrade" loop to 'while'
loop.setLooptype(DoStatement.LOOP_WHILE);
}
}
}
}
return res;
}
private static Exprent isReplaceableVar(Exprent exprent, Map<VarVersionPair, Exprent> mapVarValues) {
Exprent dest = null;
if (exprent.type == Exprent.EXPRENT_VAR) {
VarExprent var = (VarExprent)exprent;
dest = mapVarValues.get(new VarVersionPair(var));
}
return dest;
}
private static void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) {
parent.replaceExprent(var, dest);
// live sets
SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPair(var));
Set<VarVersionPair> setVars = getAllVersions(dest);
for (VarVersionPair varpaar : setVars) {
VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar);
for (Iterator<Entry<Integer, FastSparseSet<Integer>>> itent = node.live.entryList().iterator(); itent.hasNext(); ) {
Entry<Integer, FastSparseSet<Integer>> ent = itent.next();
Integer key = ent.getKey();
if (!livemap.containsKey(key)) {
itent.remove();
}
else {
FastSparseSet<Integer> set = ent.getValue();
set.complement(livemap.get(key));
if (set.isEmpty()) {
itent.remove();
}
}
}
}
}
private int[] iterateExprent(List<Exprent> lstExprents,
int index,
Exprent next,
Map<VarVersionPair, Exprent> mapVarValues,
SSAUConstructorSparseEx ssau) {
Exprent exprent = lstExprents.get(index);
int changed = 0;
for (Exprent expr : exprent.getAllExprents()) {
while (true) {
Object[] arr = iterateChildExprent(expr, exprent, next, mapVarValues, ssau);
Exprent retexpr = (Exprent)arr[0];
changed |= (Boolean)arr[1] ? 1 : 0;
boolean isReplaceable = (Boolean)arr[2];
if (retexpr != null) {
if (isReplaceable) {
replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau);
expr = retexpr;
}
else {
exprent.replaceExprent(expr, retexpr);
}
changed = 1;
}
if (!isReplaceable) {
break;
}
}
}
// no var on the highest level, so no replacing
VarExprent left = null;
Exprent right = null;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent as = (AssignmentExprent)exprent;
if (as.getLeft().type == Exprent.EXPRENT_VAR) {
left = (VarExprent)as.getLeft();
right = as.getRight();
}
}
if (left == null) {
return new int[]{-1, changed};
}
VarVersionPair leftpaar = new VarVersionPair(left);
List<VarVersionNode> usedVers = new ArrayList<>();
boolean notdom = getUsedVersions(ssau, leftpaar, usedVers);
if (!notdom && usedVers.isEmpty()) {
if (left.isStack() && (right.type == Exprent.EXPRENT_INVOCATION ||
right.type == Exprent.EXPRENT_ASSIGNMENT || right.type == Exprent.EXPRENT_NEW)) {
if (right.type == Exprent.EXPRENT_NEW) {
// new Object(); permitted
NewExprent nexpr = (NewExprent)right;
if (nexpr.isAnonymous() || nexpr.getNewType().arrayDim > 0
|| nexpr.getNewType().type != CodeConstants.TYPE_OBJECT) {
return new int[]{-1, changed};
}
}
lstExprents.set(index, right);
return new int[]{index + 1, 1};
}
else if (right.type == Exprent.EXPRENT_VAR) {
lstExprents.remove(index);
return new int[]{index, 1};
}
else {
return new int[]{-1, changed};
}
}
int useflags = right.getExprentUse();
// stack variables only
if (!left.isStack() &&
(right.type != Exprent.EXPRENT_VAR || ((VarExprent)right).isStack())) { // special case catch(... ex)
return new int[]{-1, changed};
}
if ((useflags & Exprent.MULTIPLE_USES) == 0 && (notdom || usedVers.size() > 1)) {
return new int[]{-1, changed};
}
Map<Integer, Set<VarVersionPair>> mapVars = getAllVarVersions(leftpaar, right, ssau);
boolean isSelfReference = mapVars.containsKey(leftpaar.var);
if (isSelfReference && notdom) {
return new int[]{-1, changed};
}
Set<VarVersionPair> setNextVars = next == null ? null : getAllVersions(next);
// FIXME: fix the entire method!
if (right.type != Exprent.EXPRENT_CONST &&
right.type != Exprent.EXPRENT_VAR &&
setNextVars != null &&
mapVars.containsKey(leftpaar.var)) {
for (VarVersionNode usedvar : usedVers) {
if (!setNextVars.contains(new VarVersionPair(usedvar.var, usedvar.version))) {
return new int[]{-1, changed};
}
}
}
mapVars.remove(leftpaar.var);
boolean vernotreplaced = false;
boolean verreplaced = false;
Set<VarVersionPair> setTempUsedVers = new HashSet<>();
for (VarVersionNode usedvar : usedVers) {
VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version);
if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) &&
(right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD
|| setNextVars == null || setNextVars.contains(usedver))) {
setTempUsedVers.add(usedver);
verreplaced = true;
}
else {
vernotreplaced = true;
}
}
if (isSelfReference && vernotreplaced) {
return new int[]{-1, changed};
}
else {
for (VarVersionPair usedver : setTempUsedVers) {
Exprent copy = right.copy();
if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) {
ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id));
}
mapVarValues.put(usedver, copy);
}
}
if (!notdom && !vernotreplaced) {
// remove assignment
lstExprents.remove(index);
return new int[]{index, 1};
}
else if (verreplaced) {
return new int[]{index + 1, changed};
}
else {
return new int[]{-1, changed};
}
}
private static Set<VarVersionPair> getAllVersions(Exprent exprent) {
Set<VarVersionPair> res = new HashSet<>();
List<Exprent> listTemp = new ArrayList<>(exprent.getAllExprents(true));
listTemp.add(exprent);
for (Exprent expr : listTemp) {
if (expr.type == Exprent.EXPRENT_VAR) {
VarExprent var = (VarExprent)expr;
res.add(new VarVersionPair(var));
}
}
return res;
}
private static Object[] iterateChildExprent(Exprent exprent,
Exprent parent,
Exprent next,
Map<VarVersionPair, Exprent> mapVarValues,
SSAUConstructorSparseEx ssau) {
boolean changed = false;
for (Exprent expr : exprent.getAllExprents()) {
while (true) {
Object[] arr = iterateChildExprent(expr, parent, next, mapVarValues, ssau);
Exprent retexpr = (Exprent)arr[0];
changed |= (Boolean)arr[1];
boolean isReplaceable = (Boolean)arr[2];
if (retexpr != null) {
if (isReplaceable) {
replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau);
expr = retexpr;
}
else {
exprent.replaceExprent(expr, retexpr);
}
changed = true;
}
if (!isReplaceable) {
break;
}
}
}
Exprent dest = isReplaceableVar(exprent, mapVarValues);
if (dest != null) {
return new Object[]{dest, true, true};
}
VarExprent left = null;
Exprent right = null;
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent as = (AssignmentExprent)exprent;
if (as.getLeft().type == Exprent.EXPRENT_VAR) {
left = (VarExprent)as.getLeft();
right = as.getRight();
}
}
if (left == null) {
return new Object[]{null, changed, false};
}
boolean isHeadSynchronized = false;
if (next == null && parent.type == Exprent.EXPRENT_MONITOR) {
MonitorExprent monexpr = (MonitorExprent)parent;
if (monexpr.getMonType() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) {
isHeadSynchronized = true;
}
}
// stack variable or synchronized head exprent
if (!left.isStack() && !isHeadSynchronized) {
return new Object[]{null, changed, false};
}
VarVersionPair leftpaar = new VarVersionPair(left);
List<VarVersionNode> usedVers = new ArrayList<>();
boolean notdom = getUsedVersions(ssau, leftpaar, usedVers);
if (!notdom && usedVers.isEmpty()) {
return new Object[]{right, changed, false};
}
// stack variables only
if (!left.isStack()) {
return new Object[]{null, changed, false};
}
int useflags = right.getExprentUse();
if ((useflags & Exprent.BOTH_FLAGS) != Exprent.BOTH_FLAGS) {
return new Object[]{null, changed, false};
}
Map<Integer, Set<VarVersionPair>> mapVars = getAllVarVersions(leftpaar, right, ssau);
if (mapVars.containsKey(leftpaar.var) && notdom) {
return new Object[]{null, changed, false};
}
mapVars.remove(leftpaar.var);
Set<VarVersionPair> setAllowedVars = getAllVersions(parent);
if (next != null) {
setAllowedVars.addAll(getAllVersions(next));
}
boolean vernotreplaced = false;
Set<VarVersionPair> setTempUsedVers = new HashSet<>();
for (VarVersionNode usedvar : usedVers) {
VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version);
if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) &&
(right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) {
setTempUsedVers.add(usedver);
}
else {
vernotreplaced = true;
}
}
if (!notdom && !vernotreplaced) {
for (VarVersionPair usedver : setTempUsedVers) {
Exprent copy = right.copy();
if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) {
ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id));
}
mapVarValues.put(usedver, copy);
}
// remove assignment
return new Object[]{right, changed, false};
}
return new Object[]{null, changed, false};
}
private static boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPair var, List<? super VarVersionNode> res) {
VarVersionsGraph ssuversions = ssa.getSsuversions();
VarVersionNode varnode = ssuversions.nodes.getWithKey(var);
Set<VarVersionNode> setVisited = new HashSet<>();
Set<VarVersionNode> setNotDoms = new HashSet<>();
LinkedList<VarVersionNode> stack = new LinkedList<>();
stack.add(varnode);
while (!stack.isEmpty()) {
VarVersionNode nd = stack.remove(0);
setVisited.add(nd);
if (nd != varnode && (nd.flags & VarVersionNode.FLAG_PHANTOM_FINEXIT) == 0) {
res.add(nd);
}
for (VarVersionEdge edge : nd.succs) {
VarVersionNode succ = edge.dest;
if (!setVisited.contains(edge.dest)) {
boolean isDominated = true;
for (VarVersionEdge prededge : succ.preds) {
if (!setVisited.contains(prededge.source)) {
isDominated = false;
break;
}
}
if (isDominated) {
stack.add(succ);
}
else {
setNotDoms.add(succ);
}
}
}
}
setNotDoms.removeAll(setVisited);
return !setNotDoms.isEmpty();
}
private static boolean isVersionToBeReplaced(VarVersionPair usedvar,
Map<Integer, Set<VarVersionPair>> mapVars,
SSAUConstructorSparseEx ssau,
VarVersionPair leftpaar) {
VarVersionsGraph ssuversions = ssau.getSsuversions();
SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar);
if (mapLiveVars == null) {
// dummy version, predecessor of a phi node
return false;
}
// compare protected ranges
if (!InterpreterUtil.equalObjects(ssau.getMapVersionFirstRange().get(leftpaar),
ssau.getMapVersionFirstRange().get(usedvar))) {
return false;
}
for (Entry<Integer, Set<VarVersionPair>> ent : mapVars.entrySet()) {
FastSparseSet<Integer> liveverset = mapLiveVars.get(ent.getKey());
if (liveverset == null || liveverset.isEmpty()) {
return false;
}
Set<VarVersionNode> domset = new HashSet<>();
for (VarVersionPair verpaar : ent.getValue()) {
domset.add(ssuversions.nodes.getWithKey(verpaar));
}
boolean isdom = true;
for (Integer livever : liveverset) {
VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPair(ent.getKey().intValue(), livever.intValue()));
if (!ssuversions.isDominatorSet(node, domset)) {
isdom = false;
break;
}
}
if (!isdom) {
return false;
}
}
return true;
}
private static Map<Integer, Set<VarVersionPair>> getAllVarVersions(VarVersionPair leftvar,
Exprent exprent,
SSAUConstructorSparseEx ssau) {
Map<Integer, Set<VarVersionPair>> map = new HashMap<>();
SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar);
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
for (Exprent expr : lst) {
if (expr.type == Exprent.EXPRENT_VAR) {
int varindex = ((VarExprent)expr).getIndex();
if (leftvar.var != varindex) {
if (mapLiveVars.containsKey(varindex)) {
Set<VarVersionPair> verset = new HashSet<>();
for (Integer vers : mapLiveVars.get(varindex)) {
verset.add(new VarVersionPair(varindex, vers.intValue()));
}
map.put(varindex, verset);
}
else {
throw new RuntimeException("inkonsistent live map!");
}
}
else {
map.put(varindex, null);
}
}
else if (expr.type == Exprent.EXPRENT_FIELD) {
if (ssau.getMapFieldVars().containsKey(expr.id)) {
int varindex = ssau.getMapFieldVars().get(expr.id);
if (mapLiveVars.containsKey(varindex)) {
Set<VarVersionPair> verset = new HashSet<>();
for (Integer vers : mapLiveVars.get(varindex)) {
verset.add(new VarVersionPair(varindex, vers.intValue()));
}
map.put(varindex, verset);
}
}
}
}
return map;
}
}