summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java334
1 files changed, 334 insertions, 0 deletions
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;
+ }
+}