| /* |
| * 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; |
| } |
| } |