summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java57
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java355
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java129
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java278
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java56
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java80
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java63
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java167
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java334
9 files changed, 1519 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java
new file mode 100644
index 000000000000..08c32ec398f9
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java
@@ -0,0 +1,57 @@
+/*
+ * 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.modules.decompiler.exps.Exprent;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CheckTypesResult {
+
+ private List<ExprentTypePair> lstMaxTypeExprents = new ArrayList<ExprentTypePair>();
+
+ private List<ExprentTypePair> lstMinTypeExprents = new ArrayList<ExprentTypePair>();
+
+ public void addMaxTypeExprent(Exprent exprent, VarType type) {
+ lstMaxTypeExprents.add(new ExprentTypePair(exprent, type, null));
+ }
+
+ public void addMinTypeExprent(Exprent exprent, VarType type) {
+ lstMinTypeExprents.add(new ExprentTypePair(exprent, type, null));
+ }
+
+ public List<ExprentTypePair> getLstMaxTypeExprents() {
+ return lstMaxTypeExprents;
+ }
+
+ public List<ExprentTypePair> getLstMinTypeExprents() {
+ return lstMinTypeExprents;
+ }
+
+ public static class ExprentTypePair {
+ public Exprent exprent;
+ public VarType type;
+ public VarType desttype;
+
+ public ExprentTypePair(Exprent exprent, VarType type, VarType desttype) {
+ this.exprent = exprent;
+ this.type = type;
+ this.desttype = desttype;
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java
new file mode 100644
index 000000000000..fdd6cd5015f8
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java
@@ -0,0 +1,355 @@
+/*
+ * 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;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java
new file mode 100644
index 000000000000..266ba94e1cda
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java
@@ -0,0 +1,129 @@
+/*
+ * 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.main.DecompilerContext;
+import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+
+public class VarProcessor {
+
+ private HashMap<VarVersionPaar, String> mapVarNames = new HashMap<VarVersionPaar, String>();
+
+ private VarVersionsProcessor varvers;
+
+ private HashMap<VarVersionPaar, String> thisvars = new HashMap<VarVersionPaar, String>();
+
+ private HashSet<VarVersionPaar> externvars = new HashSet<VarVersionPaar>();
+
+ public void setVarVersions(RootStatement root) {
+
+ varvers = new VarVersionsProcessor();
+ varvers.setVarVersions(root);
+ }
+
+ public void setVarDefinitions(Statement root) {
+ mapVarNames = new HashMap<VarVersionPaar, String>();
+
+ VarDefinitionHelper defproc = new VarDefinitionHelper(root,
+ (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD),
+ this);
+ defproc.setVarDefinitions();
+ }
+
+ public void setDebugVarNames(Map<Integer, String> mapDebugVarNames) {
+ if (varvers == null) {
+ return;
+ }
+
+ HashMap<Integer, Integer> mapOriginalVarIndices = varvers.getMapOriginalVarIndices();
+
+ List<VarVersionPaar> listVars = new ArrayList<VarVersionPaar>(mapVarNames.keySet());
+ Collections.sort(listVars, new Comparator<VarVersionPaar>() {
+ public int compare(VarVersionPaar o1, VarVersionPaar o2) {
+ return o1.var > o2.var ? 1 : (o1.var == o2.var ? 0 : -1);
+ }
+ });
+
+ HashMap<String, Integer> mapNames = new HashMap<String, Integer>();
+
+ for (VarVersionPaar varpaar : listVars) {
+ String name = mapVarNames.get(varpaar);
+
+ Integer orindex = mapOriginalVarIndices.get(varpaar.var);
+ if (orindex != null && mapDebugVarNames.containsKey(orindex)) {
+ name = mapDebugVarNames.get(orindex);
+ }
+
+ Integer counter = mapNames.get(name);
+ mapNames.put(name, counter == null ? counter = new Integer(0) : ++counter);
+
+ if (counter > 0) {
+ name += String.valueOf(counter);
+ }
+
+ mapVarNames.put(varpaar, name);
+ }
+ }
+
+ public void refreshVarNames(VarNamesCollector vc) {
+
+ HashMap<VarVersionPaar, String> tempVarNames = new HashMap<VarVersionPaar, String>(mapVarNames);
+ for (Entry<VarVersionPaar, String> ent : tempVarNames.entrySet()) {
+ mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue()));
+ }
+ }
+
+
+ public VarType getVarType(VarVersionPaar varpaar) {
+ return varvers == null ? null : varvers.getVarType(varpaar);
+ }
+
+ public void setVarType(VarVersionPaar varpaar, VarType type) {
+ varvers.setVarType(varpaar, type);
+ }
+
+ public String getVarName(VarVersionPaar varpaar) {
+ return mapVarNames == null ? null : mapVarNames.get(varpaar);
+ }
+
+ public void setVarName(VarVersionPaar varpaar, String name) {
+ mapVarNames.put(varpaar, name);
+ }
+
+ public int getVarFinal(VarVersionPaar varpaar) {
+ return varvers == null ? VarTypeProcessor.VAR_FINAL : varvers.getVarFinal(varpaar);
+ }
+
+ public void setVarFinal(VarVersionPaar varpaar, int finaltype) {
+ varvers.setVarFinal(varpaar, finaltype);
+ }
+
+ public HashMap<VarVersionPaar, String> getThisvars() {
+ return thisvars;
+ }
+
+ public HashSet<VarVersionPaar> getExternvars() {
+ return externvars;
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java
new file mode 100644
index 000000000000..a21d71f7e9f7
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java
@@ -0,0 +1,278 @@
+/*
+ * 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.modules.decompiler.exps.*;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
+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.RootStatement;
+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 org.jetbrains.java.decompiler.struct.gen.VarType;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+public class VarTypeProcessor {
+
+ public static final int VAR_NONFINAL = 1;
+ public static final int VAR_FINALEXPLICIT = 2;
+ public static final int VAR_FINAL = 3;
+
+ private HashMap<VarVersionPaar, VarType> mapExprentMinTypes = new HashMap<VarVersionPaar, VarType>();
+
+ private HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = new HashMap<VarVersionPaar, VarType>();
+
+ private HashMap<VarVersionPaar, Integer> mapFinalVars = new HashMap<VarVersionPaar, Integer>();
+
+ private void setInitVars(RootStatement root) {
+
+ StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD);
+
+ // method descriptor
+ boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ MethodDescriptor md = (MethodDescriptor)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_DESCRIPTOR);
+
+ if (thisvar) {
+ VarType cltype = new VarType(CodeConstants.TYPE_OBJECT, 0,
+ ((StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS)).qualifiedName);
+ mapExprentMinTypes.put(new VarVersionPaar(0, 1), cltype);
+ mapExprentMaxTypes.put(new VarVersionPaar(0, 1), cltype);
+ }
+
+ int varindex = 0;
+ for (int i = 0; i < md.params.length; i++) {
+ mapExprentMinTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]);
+ mapExprentMaxTypes.put(new VarVersionPaar(varindex + (thisvar ? 1 : 0), 1), md.params[i]);
+ varindex += md.params[i].stack_size;
+ }
+
+ // catch variables
+ LinkedList<Statement> stack = new LinkedList<Statement>();
+ stack.add(root);
+
+ while (!stack.isEmpty()) {
+ Statement stat = stack.removeFirst();
+
+ List<VarExprent> lstVars = null;
+ if (stat.type == Statement.TYPE_CATCHALL) {
+ lstVars = ((CatchAllStatement)stat).getVars();
+ }
+ else if (stat.type == Statement.TYPE_TRYCATCH) {
+ lstVars = ((CatchStatement)stat).getVars();
+ }
+
+ if (lstVars != null) {
+ for (VarExprent var : lstVars) {
+ mapExprentMinTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype());
+ mapExprentMaxTypes.put(new VarVersionPaar(var.getIndex(), 1), var.getVartype());
+ }
+ }
+
+ stack.addAll(stat.getStats());
+ }
+ }
+
+ public void calculateVarTypes(RootStatement root, DirectGraph dgraph) {
+
+ setInitVars(root);
+
+ resetExprentTypes(dgraph);
+
+ while (!processVarTypes(dgraph)) ;
+ }
+
+ private static void resetExprentTypes(DirectGraph dgraph) {
+
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ ((VarExprent)expr).setVartype(VarType.VARTYPE_UNKNOWN);
+ }
+ else if (expr.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr = (ConstExprent)expr;
+ if (cexpr.getConsttype().type_family == CodeConstants.TYPE_FAMILY_INTEGER) {
+ cexpr.setConsttype(new ConstExprent(cexpr.getIntValue(), cexpr.isBoolPermitted()).getConsttype());
+ }
+ }
+ }
+ return 0;
+ }
+ });
+ }
+
+ private boolean processVarTypes(DirectGraph dgraph) {
+
+ return dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ return checkTypeExprent(exprent) ? 0 : 1;
+ }
+ });
+ }
+
+
+ private boolean checkTypeExprent(Exprent exprent) {
+
+ for (Exprent expr : exprent.getAllExprents()) {
+ if (!checkTypeExprent(expr)) {
+ return false;
+ }
+ }
+
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ ConstExprent cexpr = (ConstExprent)exprent;
+ if (cexpr.getConsttype().type_family <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer
+ VarVersionPaar cpaar = new VarVersionPaar(cexpr.id, -1);
+ if (!mapExprentMinTypes.containsKey(cpaar)) {
+ mapExprentMinTypes.put(cpaar, cexpr.getConsttype());
+ }
+ }
+ }
+
+ CheckTypesResult result = exprent.checkExprTypeBounds();
+
+ for (CheckTypesResult.ExprentTypePair entry : result.getLstMaxTypeExprents()) {
+ if (entry.type.type_family != CodeConstants.TYPE_FAMILY_OBJECT) {
+ changeExprentType(entry.exprent, entry.type, 1);
+ }
+ }
+
+ boolean res = true;
+ for (CheckTypesResult.ExprentTypePair entry : result.getLstMinTypeExprents()) {
+ res &= changeExprentType(entry.exprent, entry.type, 0);
+ }
+
+ return res;
+ }
+
+
+ private boolean changeExprentType(Exprent exprent, VarType newtype, int minmax) {
+
+ boolean res = true;
+
+ switch (exprent.type) {
+ case Exprent.EXPRENT_CONST:
+ ConstExprent cexpr = (ConstExprent)exprent;
+ VarType consttype = cexpr.getConsttype();
+
+ if (newtype.type_family > CodeConstants.TYPE_FAMILY_INTEGER || consttype.type_family > CodeConstants.TYPE_FAMILY_INTEGER) {
+ return true;
+ }
+ else if (newtype.type_family == CodeConstants.TYPE_FAMILY_INTEGER) {
+ VarType mininteger = new ConstExprent((Integer)((ConstExprent)exprent).getValue(), false).getConsttype();
+ if (mininteger.isStrictSuperset(newtype)) {
+ newtype = mininteger;
+ }
+ }
+ case Exprent.EXPRENT_VAR:
+ VarVersionPaar varpaar = null;
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ varpaar = new VarVersionPaar(((ConstExprent)exprent).id, -1);
+ }
+ else if (exprent.type == Exprent.EXPRENT_VAR) {
+ varpaar = new VarVersionPaar((VarExprent)exprent);
+ }
+
+ if (minmax == 0) { // min
+ VarType currentMinType = mapExprentMinTypes.get(varpaar);
+ VarType newMinType;
+ if (currentMinType == null || newtype.type_family > currentMinType.type_family) {
+ newMinType = newtype;
+ }
+ else if (newtype.type_family < currentMinType.type_family) {
+ return true;
+ }
+ else {
+ newMinType = VarType.getCommonSupertype(currentMinType, newtype);
+ }
+
+ mapExprentMinTypes.put(varpaar, newMinType);
+ if (exprent.type == Exprent.EXPRENT_CONST) {
+ ((ConstExprent)exprent).setConsttype(newMinType);
+ }
+
+ if (currentMinType != null && (newMinType.type_family > currentMinType.type_family ||
+ newMinType.isStrictSuperset(currentMinType))) {
+ return false;
+ }
+ }
+ else { // max
+ VarType currentMaxType = mapExprentMaxTypes.get(varpaar);
+ VarType newMaxType;
+ if (currentMaxType == null || newtype.type_family < currentMaxType.type_family) {
+ newMaxType = newtype;
+ }
+ else if (newtype.type_family > currentMaxType.type_family) {
+ return true;
+ }
+ else {
+ newMaxType = VarType.getCommonMinType(currentMaxType, newtype);
+ }
+
+ mapExprentMaxTypes.put(varpaar, newMaxType);
+ }
+ break;
+ case Exprent.EXPRENT_ASSIGNMENT:
+ return changeExprentType(((AssignmentExprent)exprent).getRight(), newtype, minmax);
+ case Exprent.EXPRENT_FUNCTION:
+ FunctionExprent func = (FunctionExprent)exprent;
+ switch (func.getFunctype()) {
+ case FunctionExprent.FUNCTION_IIF: // FIXME:
+ res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax);
+ res &= changeExprentType(func.getLstOperands().get(2), newtype, minmax);
+ break;
+ case FunctionExprent.FUNCTION_AND:
+ case FunctionExprent.FUNCTION_OR:
+ case FunctionExprent.FUNCTION_XOR:
+ res &= changeExprentType(func.getLstOperands().get(0), newtype, minmax);
+ res &= changeExprentType(func.getLstOperands().get(1), newtype, minmax);
+ }
+ }
+
+ return res;
+ }
+
+ public HashMap<VarVersionPaar, VarType> getMapExprentMaxTypes() {
+ return mapExprentMaxTypes;
+ }
+
+ public HashMap<VarVersionPaar, VarType> getMapExprentMinTypes() {
+ return mapExprentMinTypes;
+ }
+
+ public HashMap<VarVersionPaar, Integer> getMapFinalVars() {
+ return mapFinalVars;
+ }
+
+ public void setVarType(VarVersionPaar varpaar, VarType type) {
+ mapExprentMinTypes.put(varpaar, type);
+ }
+
+ public VarType getVarType(VarVersionPaar varpaar) {
+ return mapExprentMinTypes.get(varpaar);
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java
new file mode 100644
index 000000000000..9d9798279d41
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+public class VarVersionEdge { // FIXME: can be removed?
+
+ public static final int EDGE_GENERAL = 0;
+ public static final int EDGE_PHANTOM = 1;
+
+ public int type;
+
+ public VarVersionNode source;
+
+ public VarVersionNode dest;
+
+ private int hashCode;
+
+ public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) {
+ this.type = type;
+ this.source = source;
+ this.dest = dest;
+ this.hashCode = source.hashCode() ^ dest.hashCode() + type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof VarVersionEdge)) return false;
+
+ VarVersionEdge edge = (VarVersionEdge)o;
+ return type == edge.type && source == edge.source && dest == edge.dest;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return source.toString() + " ->" + type + "-> " + dest.toString();
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java
new file mode 100644
index 000000000000..587c653dc3fb
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java
@@ -0,0 +1,80 @@
+/*
+ * 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.modules.decompiler.decompose.IGraphNode;
+import org.jetbrains.java.decompiler.util.SFormsFastMapDirect;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class VarVersionNode implements IGraphNode {
+
+ public static final int FLAG_PHANTOM_FINEXIT = 2;
+
+ public int var;
+
+ public int version;
+
+ public Set<VarVersionEdge> succs = new HashSet<VarVersionEdge>();
+
+ public Set<VarVersionEdge> preds = new HashSet<VarVersionEdge>();
+
+ public int flags;
+
+ public SFormsFastMapDirect live = new SFormsFastMapDirect();
+
+
+ public VarVersionNode(int var, int version) {
+ this.var = var;
+ this.version = version;
+ }
+
+ public VarVersionPaar getVarPaar() {
+ return new VarVersionPaar(var, version);
+ }
+
+ public List<IGraphNode> getPredecessors() {
+ List<IGraphNode> lst = new ArrayList<IGraphNode>(preds.size());
+ for (VarVersionEdge edge : preds) {
+ lst.add(edge.source);
+ }
+ return lst;
+ }
+
+ public void removeSuccessor(VarVersionEdge edge) {
+ succs.remove(edge);
+ }
+
+ public void removePredecessor(VarVersionEdge edge) {
+ preds.remove(edge);
+ }
+
+ public void addSuccessor(VarVersionEdge edge) {
+ succs.add(edge);
+ }
+
+ public void addPredecessor(VarVersionEdge edge) {
+ preds.add(edge);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + var + "_" + version + ")";
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java
new file mode 100644
index 000000000000..5f3e520a84d0
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java
@@ -0,0 +1,63 @@
+/*
+ * 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.modules.decompiler.exps.VarExprent;
+
+public class VarVersionPaar {
+
+ public int var;
+ public int version;
+
+ private int hashCode = -1;
+
+ public VarVersionPaar(int var, int version) {
+ this.var = var;
+ this.version = version;
+ }
+
+ public VarVersionPaar(Integer var, Integer version) {
+ this.var = var.intValue();
+ this.version = version.intValue();
+ }
+
+ public VarVersionPaar(VarExprent var) {
+ this.var = var.getIndex();
+ this.version = var.getVersion();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (o == null || !(o instanceof VarVersionPaar)) return false;
+
+ VarVersionPaar paar = (VarVersionPaar)o;
+ return var == paar.var && version == paar.version;
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode == -1) {
+ hashCode = this.var * 3 + this.version;
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + var + "," + version + ")";
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java
new file mode 100644
index 000000000000..f839a05819df
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java
@@ -0,0 +1,167 @@
+/*
+ * 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.modules.decompiler.decompose.GenericDominatorEngine;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;
+import org.jetbrains.java.decompiler.util.VBStyleCollection;
+
+import java.util.*;
+
+
+public class VarVersionsGraph {
+
+ public int counter = 0;
+
+ public VBStyleCollection<VarVersionNode, VarVersionPaar> nodes = new VBStyleCollection<VarVersionNode, VarVersionPaar>();
+
+ private GenericDominatorEngine engine;
+
+ public VarVersionNode createNode(VarVersionPaar ver) {
+ VarVersionNode node;
+ nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver);
+ return node;
+ }
+
+ public void addNodes(Collection<VarVersionNode> colnodes, Collection<VarVersionPaar> colpaars) {
+ nodes.addAllWithKey(colnodes, colpaars);
+ }
+
+ public boolean isDominatorSet(VarVersionNode node, HashSet<VarVersionNode> domnodes) {
+
+ if (domnodes.size() == 1) {
+ return engine.isDominator(node, domnodes.iterator().next());
+ }
+ else {
+
+ HashSet<VarVersionNode> marked = new HashSet<VarVersionNode>();
+
+ if (domnodes.contains(node)) {
+ return true;
+ }
+
+ LinkedList<VarVersionNode> lstNodes = new LinkedList<VarVersionNode>();
+ lstNodes.add(node);
+
+ while (!lstNodes.isEmpty()) {
+
+ VarVersionNode nd = lstNodes.remove(0);
+ if (marked.contains(nd)) {
+ continue;
+ }
+ else {
+ marked.add(nd);
+ }
+
+ if (nd.preds.isEmpty()) {
+ return false;
+ }
+
+ for (VarVersionEdge edge : nd.preds) {
+ VarVersionNode pred = edge.source;
+ if (!marked.contains(pred) && !domnodes.contains(pred)) {
+ lstNodes.add(pred);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void initDominators() {
+
+ final HashSet<VarVersionNode> roots = new HashSet<VarVersionNode>();
+
+ for (VarVersionNode node : nodes) {
+ if (node.preds.isEmpty()) {
+ roots.add(node);
+ }
+ }
+
+ engine = new GenericDominatorEngine(new IGraph() {
+ public List<? extends IGraphNode> getReversePostOrderList() {
+ return getReversedPostOrder(roots);
+ }
+
+ public Set<? extends IGraphNode> getRoots() {
+ return new HashSet<IGraphNode>(roots);
+ }
+ });
+
+ engine.initialize();
+ }
+
+ private static LinkedList<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) {
+
+ LinkedList<VarVersionNode> lst = new LinkedList<VarVersionNode>();
+ HashSet<VarVersionNode> setVisited = new HashSet<VarVersionNode>();
+
+ for (VarVersionNode root : roots) {
+
+ LinkedList<VarVersionNode> lstTemp = new LinkedList<VarVersionNode>();
+ addToReversePostOrderListIterative(root, lstTemp, setVisited);
+
+ lst.addAll(lstTemp);
+ }
+
+ return lst;
+ }
+
+ private static void addToReversePostOrderListIterative(VarVersionNode root, List<VarVersionNode> lst, HashSet<VarVersionNode> setVisited) {
+
+ HashMap<VarVersionNode, List<VarVersionEdge>> mapNodeSuccs = new HashMap<VarVersionNode, List<VarVersionEdge>>();
+
+ LinkedList<VarVersionNode> stackNode = new LinkedList<VarVersionNode>();
+ LinkedList<Integer> stackIndex = new LinkedList<Integer>();
+
+ stackNode.add(root);
+ stackIndex.add(0);
+
+ while (!stackNode.isEmpty()) {
+
+ VarVersionNode node = stackNode.getLast();
+ int index = stackIndex.removeLast();
+
+ setVisited.add(node);
+
+ List<VarVersionEdge> lstSuccs = mapNodeSuccs.get(node);
+ if (lstSuccs == null) {
+ mapNodeSuccs.put(node, lstSuccs = new ArrayList<VarVersionEdge>(node.succs));
+ }
+
+ for (; index < lstSuccs.size(); index++) {
+ VarVersionNode succ = lstSuccs.get(index).dest;
+
+ if (!setVisited.contains(succ)) {
+ stackIndex.add(index + 1);
+
+ stackNode.add(succ);
+ stackIndex.add(0);
+
+ break;
+ }
+ }
+
+ if (index == lstSuccs.size()) {
+ lst.add(0, node);
+
+ stackNode.removeLast();
+ }
+ }
+ }
+}
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java
new file mode 100644
index 000000000000..06b7216c86fa
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java
@@ -0,0 +1,334 @@
+/*
+ * 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.CounterContainer;
+import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
+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.sforms.DirectGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
+import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
+import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
+import org.jetbrains.java.decompiler.struct.StructMethod;
+import org.jetbrains.java.decompiler.struct.gen.VarType;
+import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class VarVersionsProcessor {
+
+ private HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>();
+
+ private VarTypeProcessor typeproc;
+
+ public void setVarVersions(RootStatement root) {
+
+ StructMethod mt = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD);
+
+ SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
+ ssa.splitVariables(root, mt);
+
+ FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
+ DirectGraph dgraph = flatthelper.buildDirectGraph(root);
+
+ // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
+
+ mergePhiVersions(ssa, dgraph);
+
+ // System.out.println("~~~~~~~~~~~~~~~~~~~~~~ \r\n"+root.toJava());
+
+ typeproc = new VarTypeProcessor();
+ typeproc.calculateVarTypes(root, dgraph);
+
+ simpleMerge(typeproc, dgraph, mt);
+
+ // FIXME: advanced merging
+
+ eliminateNonJavaTypes(typeproc);
+
+ setNewVarIndices(typeproc, dgraph);
+ }
+
+ private static void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph dgraph) {
+
+ // collect phi versions
+ List<HashSet<VarVersionPaar>> lst = new ArrayList<HashSet<VarVersionPaar>>();
+ for (Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>();
+ set.add(ent.getKey());
+ for (Integer vers : ent.getValue()) {
+ set.add(new VarVersionPaar(ent.getKey().var, vers.intValue()));
+ }
+
+ for (int i = lst.size() - 1; i >= 0; i--) {
+ HashSet<VarVersionPaar> tset = lst.get(i);
+ HashSet<VarVersionPaar> intersection = new HashSet<VarVersionPaar>(set);
+ intersection.retainAll(tset);
+
+ if (!intersection.isEmpty()) {
+ set.addAll(tset);
+ lst.remove(i);
+ }
+ }
+
+ lst.add(set);
+ }
+
+ final HashMap<VarVersionPaar, Integer> phivers = new HashMap<VarVersionPaar, Integer>();
+ for (HashSet<VarVersionPaar> set : lst) {
+ int min = Integer.MAX_VALUE;
+ for (VarVersionPaar paar : set) {
+ if (paar.version < min) {
+ min = paar.version;
+ }
+ }
+
+ for (VarVersionPaar paar : set) {
+ phivers.put(new VarVersionPaar(paar.var, paar.version), min);
+ }
+ }
+
+
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent var = (VarExprent)expr;
+ Integer vers = phivers.get(new VarVersionPaar(var));
+ if (vers != null) {
+ var.setVersion(vers);
+ }
+ }
+ }
+ return 0;
+ }
+ });
+ }
+
+ private static void eliminateNonJavaTypes(VarTypeProcessor typeproc) {
+
+ HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes();
+ HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes();
+
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet());
+ for (VarVersionPaar paar : set) {
+ VarType type = mapExprentMinTypes.get(paar);
+ VarType maxtype = mapExprentMaxTypes.get(paar);
+
+ if (type.type == CodeConstants.TYPE_BYTECHAR || type.type == CodeConstants.TYPE_SHORTCHAR) {
+ if (maxtype != null && maxtype.type == CodeConstants.TYPE_CHAR) {
+ type = VarType.VARTYPE_CHAR;
+ }
+ else {
+ type = type.type == CodeConstants.TYPE_BYTECHAR ? VarType.VARTYPE_BYTE : VarType.VARTYPE_SHORT;
+ }
+ mapExprentMinTypes.put(paar, type);
+ //} else if(type.type == CodeConstants.TYPE_CHAR && (maxtype == null || maxtype.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int
+ // mapExprentMinTypes.put(paar, VarType.VARTYPE_INT);
+ }
+ else if (type.type == CodeConstants.TYPE_NULL) {
+ mapExprentMinTypes.put(paar, VarType.VARTYPE_OBJECT);
+ }
+ }
+ }
+
+ private static void simpleMerge(VarTypeProcessor typeproc, DirectGraph dgraph, StructMethod mt) {
+
+ HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes();
+ HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes();
+
+ HashMap<Integer, HashSet<Integer>> mapVarVersions = new HashMap<Integer, HashSet<Integer>>();
+
+ for (VarVersionPaar varpaar : mapExprentMinTypes.keySet()) {
+ if (varpaar.version >= 0) { // don't merge constants
+ HashSet<Integer> set = mapVarVersions.get(varpaar.var);
+ if (set == null) {
+ set = new HashSet<Integer>();
+ mapVarVersions.put(varpaar.var, set);
+ }
+ set.add(varpaar.version);
+ }
+ }
+
+ boolean is_method_static = mt.hasModifier(CodeConstants.ACC_STATIC);
+
+ final HashMap<VarVersionPaar, Integer> mapMergedVersions = new HashMap<VarVersionPaar, Integer>();
+
+ for (Entry<Integer, HashSet<Integer>> ent : mapVarVersions.entrySet()) {
+
+ if (ent.getValue().size() > 1) {
+ List<Integer> lstVersions = new ArrayList<Integer>(ent.getValue());
+ Collections.sort(lstVersions);
+
+ for (int i = 0; i < lstVersions.size(); i++) {
+ VarVersionPaar firstpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(i));
+ VarType firsttype = mapExprentMinTypes.get(firstpaar);
+
+ if (firstpaar.var == 0 && firstpaar.version == 1 && !is_method_static) {
+ continue; // don't merge 'this' variable
+ }
+
+ for (int j = i + 1; j < lstVersions.size(); j++) {
+ VarVersionPaar secpaar = new VarVersionPaar(ent.getKey(), lstVersions.get(j));
+ VarType sectype = mapExprentMinTypes.get(secpaar);
+
+ if (firsttype.equals(sectype) || (firsttype.equals(VarType.VARTYPE_NULL) && sectype.type == CodeConstants.TYPE_OBJECT)
+ || (sectype.equals(VarType.VARTYPE_NULL) && firsttype.type == CodeConstants.TYPE_OBJECT)) {
+
+ VarType firstMaxType = mapExprentMaxTypes.get(firstpaar);
+ VarType secMaxType = mapExprentMaxTypes.get(secpaar);
+ mapExprentMaxTypes.put(firstpaar, firstMaxType == null ? secMaxType :
+ (secMaxType == null ? firstMaxType : VarType.getCommonMinType(firstMaxType, secMaxType)));
+
+
+ mapMergedVersions.put(secpaar, firstpaar.version);
+ mapExprentMaxTypes.remove(secpaar);
+ mapExprentMinTypes.remove(secpaar);
+
+ if (firsttype.equals(VarType.VARTYPE_NULL)) {
+ mapExprentMinTypes.put(firstpaar, sectype);
+ firsttype = sectype;
+ }
+
+ typeproc.getMapFinalVars().put(firstpaar, VarTypeProcessor.VAR_NONFINAL);
+
+ lstVersions.remove(j);
+ j--;
+ }
+ }
+ }
+ }
+ }
+
+ if (!mapMergedVersions.isEmpty()) {
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent varex = (VarExprent)expr;
+ Integer newversion = mapMergedVersions.get(new VarVersionPaar(varex));
+ if (newversion != null) {
+ varex.setVersion(newversion);
+ }
+ }
+ }
+
+ return 0;
+ }
+ });
+ }
+ }
+
+ private void setNewVarIndices(VarTypeProcessor typeproc, DirectGraph dgraph) {
+
+ final HashMap<VarVersionPaar, VarType> mapExprentMaxTypes = typeproc.getMapExprentMaxTypes();
+ HashMap<VarVersionPaar, VarType> mapExprentMinTypes = typeproc.getMapExprentMinTypes();
+ HashMap<VarVersionPaar, Integer> mapFinalVars = typeproc.getMapFinalVars();
+
+ CounterContainer ccon = DecompilerContext.getCounterContainer();
+
+ final HashMap<VarVersionPaar, Integer> mapVarPaar = new HashMap<VarVersionPaar, Integer>();
+ HashMap<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>();
+
+ // map var-version paars on new var indexes
+ HashSet<VarVersionPaar> set = new HashSet<VarVersionPaar>(mapExprentMinTypes.keySet());
+ for (VarVersionPaar vpaar : set) {
+
+ if (vpaar.version >= 0) {
+ int newindex = vpaar.version == 1 ? vpaar.var :
+ ccon.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
+
+ VarVersionPaar newvar = new VarVersionPaar(newindex, 0);
+
+ mapExprentMinTypes.put(newvar, mapExprentMinTypes.get(vpaar));
+ mapExprentMaxTypes.put(newvar, mapExprentMaxTypes.get(vpaar));
+
+ if (mapFinalVars.containsKey(vpaar)) {
+ mapFinalVars.put(newvar, mapFinalVars.remove(vpaar));
+ }
+
+ mapVarPaar.put(vpaar, newindex);
+ mapOriginalVarIndices.put(newindex, vpaar.var);
+ }
+ }
+
+ // set new vars
+ dgraph.iterateExprents(new DirectGraph.ExprentIterator() {
+ public int processExprent(Exprent exprent) {
+ List<Exprent> lst = exprent.getAllExprents(true);
+ lst.add(exprent);
+
+ for (Exprent expr : lst) {
+ if (expr.type == Exprent.EXPRENT_VAR) {
+ VarExprent varex = (VarExprent)expr;
+ Integer newvarindex = mapVarPaar.get(new VarVersionPaar(varex));
+ if (newvarindex != null) {
+ varex.setIndex(newvarindex);
+ varex.setVersion(0);
+ }
+ }
+ else if (expr.type == Exprent.EXPRENT_CONST) {
+ VarType maxType = mapExprentMaxTypes.get(new VarVersionPaar(expr.id, -1));
+ if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) {
+ ((ConstExprent)expr).setConsttype(maxType);
+ }
+ }
+ }
+
+ return 0;
+ }
+ });
+
+ this.mapOriginalVarIndices = mapOriginalVarIndices;
+ }
+
+ public VarType getVarType(VarVersionPaar varpaar) {
+ return typeproc == null ? null : typeproc.getVarType(varpaar);
+ }
+
+ public void setVarType(VarVersionPaar varpaar, VarType type) {
+ typeproc.setVarType(varpaar, type);
+ }
+
+ public int getVarFinal(VarVersionPaar varpaar) {
+
+ int ret = VarTypeProcessor.VAR_FINAL;
+ if (typeproc != null) {
+ Integer fin = typeproc.getMapFinalVars().get(varpaar);
+ ret = fin == null ? VarTypeProcessor.VAR_FINAL : fin.intValue();
+ }
+
+ return ret;
+ }
+
+ public void setVarFinal(VarVersionPaar varpaar, int finaltype) {
+ typeproc.getMapFinalVars().put(varpaar, finaltype);
+ }
+
+ public HashMap<Integer, Integer> getMapOriginalVarIndices() {
+ return mapOriginalVarIndices;
+ }
+}