/* * 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.exps; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.TextBuffer; import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.ListStack; public class FunctionExprent extends Exprent { public static final int FUNCTION_ADD = 0; public static final int FUNCTION_SUB = 1; public static final int FUNCTION_MUL = 2; public static final int FUNCTION_DIV = 3; public static final int FUNCTION_AND = 4; public static final int FUNCTION_OR = 5; public static final int FUNCTION_XOR = 6; public static final int FUNCTION_REM = 7; public static final int FUNCTION_SHL = 8; public static final int FUNCTION_SHR = 9; public static final int FUNCTION_USHR = 10; public static final int FUNCTION_BITNOT = 11; public static final int FUNCTION_BOOLNOT = 12; public static final int FUNCTION_NEG = 13; public final static int FUNCTION_I2L = 14; public final static int FUNCTION_I2F = 15; public final static int FUNCTION_I2D = 16; public final static int FUNCTION_L2I = 17; public final static int FUNCTION_L2F = 18; public final static int FUNCTION_L2D = 19; public final static int FUNCTION_F2I = 20; public final static int FUNCTION_F2L = 21; public final static int FUNCTION_F2D = 22; public final static int FUNCTION_D2I = 23; public final static int FUNCTION_D2L = 24; public final static int FUNCTION_D2F = 25; public final static int FUNCTION_I2B = 26; public final static int FUNCTION_I2C = 27; public final static int FUNCTION_I2S = 28; public final static int FUNCTION_CAST = 29; public final static int FUNCTION_INSTANCEOF = 30; public final static int FUNCTION_ARRAYLENGTH = 31; public final static int FUNCTION_IMM = 32; public final static int FUNCTION_MMI = 33; public final static int FUNCTION_IPP = 34; public final static int FUNCTION_PPI = 35; public final static int FUNCTION_IIF = 36; public final static int FUNCTION_LCMP = 37; public final static int FUNCTION_FCMPL = 38; public final static int FUNCTION_FCMPG = 39; public final static int FUNCTION_DCMPL = 40; public final static int FUNCTION_DCMPG = 41; public static final int FUNCTION_EQ = 42; public static final int FUNCTION_NE = 43; public static final int FUNCTION_LT = 44; public static final int FUNCTION_GE = 45; public static final int FUNCTION_GT = 46; public static final int FUNCTION_LE = 47; public static final int FUNCTION_CADD = 48; public static final int FUNCTION_COR = 49; public static final int FUNCTION_STRCONCAT = 50; private static final VarType[] types = new VarType[]{ VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_BYTE, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT }; private static final String[] operators = new String[]{ " + ", " - ", " * ", " / ", " & ", " | ", " ^ ", " % ", " << ", " >> ", " >>> ", " == ", " != ", " < ", " >= ", " > ", " <= ", " && ", " || ", " + " }; private static final int[] precedence = new int[]{ 3, // FUNCTION_ADD 3, // FUNCTION_SUB 2, // FUNCTION_MUL 2, // FUNCTION_DIV 7, // FUNCTION_AND 9, // FUNCTION_OR 8, // FUNCTION_XOR 2, // FUNCTION_REM 4, // FUNCTION_SHL 4, // FUNCTION_SHR 4, // FUNCTION_USHR 1, // FUNCTION_BITNOT 1, // FUNCTION_BOOLNOT 1, // FUNCTION_NEG 1, // FUNCTION_I2L 1, // FUNCTION_I2F 1, // FUNCTION_I2D 1, // FUNCTION_L2I 1, // FUNCTION_L2F 1, // FUNCTION_L2D 1, // FUNCTION_F2I 1, // FUNCTION_F2L 1, // FUNCTION_F2D 1, // FUNCTION_D2I 1, // FUNCTION_D2L 1, // FUNCTION_D2F 1, // FUNCTION_I2B 1, // FUNCTION_I2C 1, // FUNCTION_I2S 1, // FUNCTION_CAST 6, // FUNCTION_INSTANCEOF 0, // FUNCTION_ARRAYLENGTH 1, // FUNCTION_IMM 1, // FUNCTION_MMI 1, // FUNCTION_IPP 1, // FUNCTION_PPI 12, // FUNCTION_IFF -1, // FUNCTION_LCMP -1, // FUNCTION_FCMPL -1, // FUNCTION_FCMPG -1, // FUNCTION_DCMPL -1, // FUNCTION_DCMPG 6, // FUNCTION_EQ = 41; 6, // FUNCTION_NE = 42; 5, // FUNCTION_LT = 43; 5, // FUNCTION_GE = 44; 5, // FUNCTION_GT = 45; 5, // FUNCTION_LE = 46; 10, // FUNCTION_CADD = 47; 11, // FUNCTION_COR = 48; 3 // FUNCTION_STRCONCAT = 49; }; private static final HashSet<Integer> associativity = new HashSet<Integer>(Arrays.asList(new Integer[]{FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STRCONCAT})); private int functype; private VarType implicitType; private List<Exprent> lstOperands = new ArrayList<Exprent>(); { this.type = EXPRENT_FUNCTION; } public FunctionExprent(int functype, ListStack<Exprent> stack, Set<Integer> bytecode_offsets) { this.functype = functype; if (functype >= FUNCTION_BITNOT && functype <= FUNCTION_PPI && functype != FUNCTION_CAST && functype != FUNCTION_INSTANCEOF) { lstOperands.add(stack.pop()); } else if (functype == FUNCTION_IIF) { throw new RuntimeException("no direct instantiation possible"); } else { Exprent expr = stack.pop(); lstOperands.add(stack.pop()); lstOperands.add(expr); } addBytecodeOffsets(bytecode_offsets); } public FunctionExprent(int functype, List<Exprent> operands, Set<Integer> bytecode_offsets) { this.functype = functype; this.lstOperands = operands; addBytecodeOffsets(bytecode_offsets); } public VarType getExprType() { VarType exprType = null; if (functype <= FUNCTION_NEG || functype == FUNCTION_IPP || functype == FUNCTION_PPI || functype == FUNCTION_IMM || functype == FUNCTION_MMI) { VarType type1 = lstOperands.get(0).getExprType(); VarType type2 = null; if (lstOperands.size() > 1) { type2 = lstOperands.get(1).getExprType(); } switch (functype) { case FUNCTION_IMM: case FUNCTION_MMI: case FUNCTION_IPP: case FUNCTION_PPI: exprType = implicitType; break; case FUNCTION_BOOLNOT: exprType = VarType.VARTYPE_BOOLEAN; break; case FUNCTION_SHL: case FUNCTION_SHR: case FUNCTION_USHR: case FUNCTION_BITNOT: case FUNCTION_NEG: exprType = getMaxVarType(new VarType[]{type1}); break; case FUNCTION_ADD: case FUNCTION_SUB: case FUNCTION_MUL: case FUNCTION_DIV: case FUNCTION_REM: exprType = getMaxVarType(new VarType[]{type1, type2}); break; case FUNCTION_AND: case FUNCTION_OR: case FUNCTION_XOR: if (type1.type == CodeConstants.TYPE_BOOLEAN & type2.type == CodeConstants.TYPE_BOOLEAN) { exprType = VarType.VARTYPE_BOOLEAN; } else { exprType = getMaxVarType(new VarType[]{type1, type2}); } } } else if (functype == FUNCTION_CAST) { exprType = lstOperands.get(1).getExprType(); } else if (functype == FUNCTION_IIF) { Exprent param1 = lstOperands.get(1); Exprent param2 = lstOperands.get(2); VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType()); if (param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST && supertype.type != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) { exprType = VarType.VARTYPE_INT; } else { exprType = supertype; } } else if (functype == FUNCTION_STRCONCAT) { exprType = VarType.VARTYPE_STRING; } else if (functype >= FUNCTION_EQ || functype == FUNCTION_INSTANCEOF) { exprType = VarType.VARTYPE_BOOLEAN; } else if (functype >= FUNCTION_ARRAYLENGTH) { exprType = VarType.VARTYPE_INT; } else { exprType = types[functype - FUNCTION_I2L]; } return exprType; } public int getExprentUse() { if (functype >= FUNCTION_IMM && functype <= FUNCTION_PPI) { return 0; } else { int ret = Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE; for (Exprent expr : lstOperands) { ret &= expr.getExprentUse(); } return ret; } } public CheckTypesResult checkExprTypeBounds() { CheckTypesResult result = new CheckTypesResult(); Exprent param1 = lstOperands.get(0); VarType type1 = param1.getExprType(); Exprent param2 = null; VarType type2 = null; if (lstOperands.size() > 1) { param2 = lstOperands.get(1); type2 = param2.getExprType(); } switch (functype) { case FUNCTION_IIF: VarType supertype = getExprType(); if (supertype == null) { supertype = getExprType(); } result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN); result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.type_family)); result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.type_family)); break; case FUNCTION_I2L: case FUNCTION_I2F: case FUNCTION_I2D: case FUNCTION_I2B: case FUNCTION_I2C: case FUNCTION_I2S: result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); result.addMaxTypeExprent(param1, VarType.VARTYPE_INT); break; case FUNCTION_IMM: case FUNCTION_IPP: case FUNCTION_MMI: case FUNCTION_PPI: result.addMinTypeExprent(param1, implicitType); result.addMaxTypeExprent(param1, implicitType); break; case FUNCTION_ADD: case FUNCTION_SUB: case FUNCTION_MUL: case FUNCTION_DIV: case FUNCTION_REM: case FUNCTION_SHL: case FUNCTION_SHR: case FUNCTION_USHR: case FUNCTION_LT: case FUNCTION_GE: case FUNCTION_GT: case FUNCTION_LE: result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); case FUNCTION_BITNOT: // case FUNCTION_BOOLNOT: case FUNCTION_NEG: result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); break; case FUNCTION_AND: case FUNCTION_OR: case FUNCTION_XOR: case FUNCTION_EQ: case FUNCTION_NE: { if (type1.type == CodeConstants.TYPE_BOOLEAN) { if (type2.isStrictSuperset(type1)) { result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); } else { // both are booleans boolean param1_false_boolean = type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue()); boolean param2_false_boolean = type1.isFalseBoolean() || (param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue()); if (param1_false_boolean || param2_false_boolean) { result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR); result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); } } } else if (type2.type == CodeConstants.TYPE_BOOLEAN) { if (type1.isStrictSuperset(type2)) { result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR); } } } } return result; } public List<Exprent> getAllExprents() { List<Exprent> lst = new ArrayList<Exprent>(); lst.addAll(lstOperands); return lst; } @Override public Exprent copy() { List<Exprent> lst = new ArrayList<Exprent>(); for (Exprent expr : lstOperands) { lst.add(expr.copy()); } FunctionExprent func = new FunctionExprent(functype, lst, bytecode); func.setImplicitType(implicitType); return func; } public boolean equals(Object o) { if (o == this) return true; if (o == null || !(o instanceof FunctionExprent)) return false; FunctionExprent fe = (FunctionExprent)o; return functype == fe.getFunctype() && InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant } public void replaceExprent(Exprent oldexpr, Exprent newexpr) { for (int i = 0; i < lstOperands.size(); i++) { if (oldexpr == lstOperands.get(i)) { lstOperands.set(i, newexpr); } } } @Override public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); if (functype <= FUNCTION_USHR) { return wrapOperandString(lstOperands.get(0), false, indent, tracer) .append(operators[functype]) .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)); } if (functype >= FUNCTION_EQ) { return wrapOperandString(lstOperands.get(0), false, indent, tracer) .append(operators[functype - FUNCTION_EQ + 11]) .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)); } switch (functype) { case FUNCTION_BITNOT: return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("~"); case FUNCTION_BOOLNOT: return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("!"); case FUNCTION_NEG: return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("-"); case FUNCTION_CAST: return lstOperands.get(1).toJava(indent, tracer).enclose("(", ")").append(wrapOperandString(lstOperands.get(0), true, indent, tracer)); case FUNCTION_ARRAYLENGTH: Exprent arr = lstOperands.get(0); TextBuffer res = wrapOperandString(arr, false, indent, tracer); if (arr.getExprType().arraydim == 0) { VarType objarr = VarType.VARTYPE_OBJECT.copy(); objarr.arraydim = 1; // type family does not change res.enclose("((" + ExprProcessor.getCastTypeName(objarr) + ")", ")"); } return res.append(".length"); case FUNCTION_IIF: return wrapOperandString(lstOperands.get(0), true, indent, tracer) .append("?") .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) .append(":") .append(wrapOperandString(lstOperands.get(2), true, indent, tracer)); case FUNCTION_IPP: return wrapOperandString(lstOperands.get(0), true, indent, tracer).append("++"); case FUNCTION_PPI: return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("++"); case FUNCTION_IMM: return wrapOperandString(lstOperands.get(0), true, indent, tracer).append("--"); case FUNCTION_MMI: return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("--"); case FUNCTION_INSTANCEOF: return wrapOperandString(lstOperands.get(0), true, indent, tracer).append(" instanceof ").append(wrapOperandString(lstOperands.get(1), true, indent, tracer)); case FUNCTION_LCMP: // shouldn't appear in the final code return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__lcmp__(") .append(",") .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) .append(")"); case FUNCTION_FCMPL: // shouldn't appear in the final code return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__fcmpl__(") .append(",") .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) .append(")"); case FUNCTION_FCMPG: // shouldn't appear in the final code return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__fcmpg__(") .append(",") .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) .append(")"); case FUNCTION_DCMPL: // shouldn't appear in the final code return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__dcmpl__(") .append(",") .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) .append(")"); case FUNCTION_DCMPG: // shouldn't appear in the final code return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("__dcmpg__(") .append(",") .append(wrapOperandString(lstOperands.get(1), true, indent, tracer)) .append(")"); } if (functype <= FUNCTION_I2S) { return wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend("(" + ExprProcessor.getTypeName(types[functype - FUNCTION_I2L]) + ")"); } // return "<unknown function>"; throw new RuntimeException("invalid function"); } public int getPrecedence() { return getPrecedence(functype); } public static int getPrecedence(int func) { return precedence[func]; } public VarType getSimpleCastType() { return types[functype - FUNCTION_I2L]; } private TextBuffer wrapOperandString(Exprent expr, boolean eq, int indent, BytecodeMappingTracer tracer) { int myprec = getPrecedence(); int exprprec = expr.getPrecedence(); boolean parentheses = exprprec > myprec; if (!parentheses && eq) { parentheses = (exprprec == myprec); if (parentheses) { if (expr.type == Exprent.EXPRENT_FUNCTION && ((FunctionExprent)expr).getFunctype() == functype) { parentheses = !associativity.contains(functype); } } } TextBuffer res = expr.toJava(indent, tracer); if (parentheses) { res.enclose("(", ")"); } return res; } private static VarType getMaxVarType(VarType[] arr) { int[] types = new int[]{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG}; VarType[] vartypes = new VarType[]{VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG}; for (int i = 0; i < types.length; i++) { for (int j = 0; j < arr.length; j++) { if (arr[j].type == types[i]) { return vartypes[i]; } } } return VarType.VARTYPE_INT; } // ***************************************************************************** // getter and setter methods // ***************************************************************************** public int getFunctype() { return functype; } public void setFunctype(int functype) { this.functype = functype; } public List<Exprent> getLstOperands() { return lstOperands; } public void setLstOperands(List<Exprent> lstOperands) { this.lstOperands = lstOperands; } public VarType getImplicitType() { return implicitType; } public void setImplicitType(VarType implicitType) { this.implicitType = implicitType; } }