blob: fdd6cd5015f80069a14f894f4ca83d87b5df0f78 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.java.decompiler.modules.decompiler.vars;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import java.util.*;
import java.util.Map.Entry;
public class VarDefinitionHelper {
private HashMap<Integer, Statement> mapVarDefStatements;
// statement.id, defined vars
private HashMap<Integer, HashSet<Integer>> mapStatementVars;
private HashSet<Integer> implDefVars;
private VarProcessor varproc;
public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) {
mapVarDefStatements = new HashMap<Integer, Statement>();
mapStatementVars = new HashMap<Integer, HashSet<Integer>>();
implDefVars = new HashSet<Integer>();
this.varproc = varproc;
VarNamesCollector vc = DecompilerContext.getVarNamesCollector();
boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
int paramcount = 0;
if (thisvar) {
paramcount = 1;
}
paramcount += md.params.length;
// method parameters are implicitly defined
int varindex = 0;
for (int i = 0; i < paramcount; i++) {
implDefVars.add(varindex);
varproc.setVarName(new VarVersionPaar(varindex, 0), vc.getFreeName(varindex));
if (thisvar) {
if (i == 0) {
varindex++;
}
else {
varindex += md.params[i - 1].stack_size;
}
}
else {
varindex += md.params[i].stack_size;
}
}
if (thisvar) {
StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS);
varproc.getThisvars().put(new VarVersionPaar(0, 0), current_class.qualifiedName);
varproc.setVarName(new VarVersionPaar(0, 0), "this");
vc.addName("this");
}
// catch variables are implicitly defined
LinkedList<Statement> stack = new LinkedList<Statement>();
stack.add(root);
while (!stack.isEmpty()) {
Statement st = stack.removeFirst();
List<VarExprent> lstVars = null;
if (st.type == Statement.TYPE_CATCHALL) {
lstVars = ((CatchAllStatement)st).getVars();
}
else if (st.type == Statement.TYPE_TRYCATCH) {
lstVars = ((CatchStatement)st).getVars();
}
if (lstVars != null) {
for (VarExprent var : lstVars) {
implDefVars.add(var.getIndex());
varproc.setVarName(new VarVersionPaar(var), vc.getFreeName(var.getIndex()));
var.setDefinition(true);
}
}
stack.addAll(st.getStats());
}
initStatement(root);
}
public void setVarDefinitions() {
VarNamesCollector vc = DecompilerContext.getVarNamesCollector();
for (Entry<Integer, Statement> en : mapVarDefStatements.entrySet()) {
Statement stat = en.getValue();
Integer index = en.getKey();
if (implDefVars.contains(index)) {
// already implicitly defined
continue;
}
varproc.setVarName(new VarVersionPaar(index.intValue(), 0), vc.getFreeName(index));
// special case for
if (stat.type == Statement.TYPE_DO) {
DoStatement dstat = (DoStatement)stat;
if (dstat.getLooptype() == DoStatement.LOOP_FOR) {
if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) {
continue;
}
else {
List<Exprent> lstSpecial = Arrays.asList(dstat.getConditionExprent(), dstat.getIncExprent());
for (VarExprent var : getAllVars(lstSpecial)) {
if (var.getIndex() == index.intValue()) {
stat = stat.getParent();
break;
}
}
}
}
}
Statement first = findFirstBlock(stat, index);
List<Exprent> lst;
if (first == null) {
lst = stat.getVarDefinitions();
}
else if (first.getExprents() == null) {
lst = first.getVarDefinitions();
}
else {
lst = first.getExprents();
}
boolean defset = false;
// search for the first assignement to var [index]
int addindex = 0;
for (Exprent expr : lst) {
if (setDefinition(expr, index)) {
defset = true;
break;
}
else {
boolean foundvar = false;
for (Exprent exp : expr.getAllExprents(true)) {
if (exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) {
foundvar = true;
break;
}
}
if (foundvar) {
break;
}
}
addindex++;
}
if (!defset) {
VarExprent var = new VarExprent(index.intValue(), varproc.getVarType(new VarVersionPaar(index.intValue(), 0)), varproc);
var.setDefinition(true);
lst.add(addindex, var);
}
}
}
// *****************************************************************************
// private methods
// *****************************************************************************
private Statement findFirstBlock(Statement stat, Integer varindex) {
LinkedList<Statement> stack = new LinkedList<Statement>();
stack.add(stat);
while (!stack.isEmpty()) {
Statement st = stack.remove(0);
if (stack.isEmpty() || mapStatementVars.get(st.id).contains(varindex)) {
if (st.isLabeled() && !stack.isEmpty()) {
return st;
}
if (st.getExprents() != null) {
return st;
}
else {
stack.clear();
switch (st.type) {
case Statement.TYPE_SEQUENCE:
stack.addAll(0, st.getStats());
break;
case Statement.TYPE_IF:
case Statement.TYPE_ROOT:
case Statement.TYPE_SWITCH:
case Statement.TYPE_SYNCRONIZED:
stack.add(st.getFirst());
break;
default:
return st;
}
}
}
}
return null;
}
private Set<Integer> initStatement(Statement stat) {
HashMap<Integer, Integer> mapCount = new HashMap<Integer, Integer>();
List<VarExprent> condlst;
if (stat.getExprents() == null) {
// recurse on children statements
List<Integer> childVars = new ArrayList<Integer>();
List<Exprent> currVars = new ArrayList<Exprent>();
for (Object obj : stat.getSequentialObjects()) {
if (obj instanceof Statement) {
Statement st = (Statement)obj;
childVars.addAll(initStatement(st));
if (st.type == DoStatement.TYPE_DO) {
DoStatement dost = (DoStatement)st;
if (dost.getLooptype() != DoStatement.LOOP_FOR &&
dost.getLooptype() != DoStatement.LOOP_DO) {
currVars.add(dost.getConditionExprent());
}
}
else if (st.type == DoStatement.TYPE_CATCHALL) {
CatchAllStatement fin = (CatchAllStatement)st;
if (fin.isFinally() && fin.getMonitor() != null) {
currVars.add(fin.getMonitor());
}
}
}
else if (obj instanceof Exprent) {
currVars.add((Exprent)obj);
}
}
// children statements
for (Integer index : childVars) {
Integer count = mapCount.get(index);
if (count == null) {
count = new Integer(0);
}
mapCount.put(index, new Integer(count.intValue() + 1));
}
condlst = getAllVars(currVars);
}
else {
condlst = getAllVars(stat.getExprents());
}
// this statement
for (VarExprent var : condlst) {
mapCount.put(new Integer(var.getIndex()), new Integer(2));
}
HashSet<Integer> set = new HashSet<Integer>(mapCount.keySet());
// put all variables defined in this statement into the set
for (Entry<Integer, Integer> en : mapCount.entrySet()) {
if (en.getValue().intValue() > 1) {
mapVarDefStatements.put(en.getKey(), stat);
}
}
mapStatementVars.put(stat.id, set);
return set;
}
private static List<VarExprent> getAllVars(List<Exprent> lst) {
List<VarExprent> res = new ArrayList<VarExprent>();
List<Exprent> listTemp = new ArrayList<Exprent>();
for (Exprent expr : lst) {
listTemp.addAll(expr.getAllExprents(true));
listTemp.add(expr);
}
for (Exprent exprent : listTemp) {
if (exprent.type == Exprent.EXPRENT_VAR) {
res.add((VarExprent)exprent);
}
}
return res;
}
private static boolean setDefinition(Exprent expr, Integer index) {
if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {
Exprent left = ((AssignmentExprent)expr).getLeft();
if (left.type == Exprent.EXPRENT_VAR) {
VarExprent var = (VarExprent)left;
if (var.getIndex() == index.intValue()) {
var.setDefinition(true);
return true;
}
}
}
return false;
}
}