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