summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java615
1 files changed, 615 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java
new file mode 100644
index 000000000000..4fa2ab385cee
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java
@@ -0,0 +1,615 @@
+/*
+ * 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 org.jetbrains.java.decompiler.code.CodeConstants;
+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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+
+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) {
+ 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);
+ }
+ }
+
+ public FunctionExprent(int functype, List<Exprent> operands) {
+ this.functype = functype;
+ this.lstOperands = operands;
+ }
+
+ 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;
+ }
+
+ public Exprent copy() {
+ List<Exprent> lst = new ArrayList<Exprent>();
+ for (Exprent expr : lstOperands) {
+ lst.add(expr.copy());
+ }
+ FunctionExprent func = new FunctionExprent(functype, lst);
+ 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);
+ }
+ }
+ }
+
+ public String toJava(int indent) {
+
+ if (functype <= FUNCTION_USHR) {
+ return wrapOperandString(lstOperands.get(0), false, indent) + operators[functype] +
+ wrapOperandString(lstOperands.get(1), true, indent);
+ }
+
+ if (functype >= FUNCTION_EQ) {
+ return wrapOperandString(lstOperands.get(0), false, indent) + operators[functype - FUNCTION_EQ + 11] +
+ wrapOperandString(lstOperands.get(1), true, indent);
+ }
+
+ switch (functype) {
+ case FUNCTION_BITNOT:
+ return "~" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_BOOLNOT:
+ return "!" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_NEG:
+ return "-" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_CAST:
+ return "(" + lstOperands.get(1).toJava(indent) + ")" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_ARRAYLENGTH:
+ Exprent arr = lstOperands.get(0);
+
+ String res = wrapOperandString(arr, false, indent);
+ if (arr.getExprType().arraydim == 0) {
+ VarType objarr = VarType.VARTYPE_OBJECT.copy();
+ objarr.arraydim = 1; // type family does not change
+
+ res = "((" + ExprProcessor.getCastTypeName(objarr) + ")" + res + ")";
+ }
+ return res + ".length";
+ case FUNCTION_IIF:
+ return wrapOperandString(lstOperands.get(0), true, indent) + "?" + wrapOperandString(lstOperands.get(1), true, indent) + ":" +
+ wrapOperandString(lstOperands.get(2), true, indent);
+ case FUNCTION_IPP:
+ return wrapOperandString(lstOperands.get(0), true, indent) + "++";
+ case FUNCTION_PPI:
+ return "++" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_IMM:
+ return wrapOperandString(lstOperands.get(0), true, indent) + "--";
+ case FUNCTION_MMI:
+ return "--" + wrapOperandString(lstOperands.get(0), true, indent);
+ case FUNCTION_INSTANCEOF:
+ return wrapOperandString(lstOperands.get(0), true, indent) + " instanceof " + wrapOperandString(lstOperands.get(1), true, indent);
+ case FUNCTION_LCMP: // shouldn't appear in the final code
+ return "__lcmp__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_FCMPL: // shouldn't appear in the final code
+ return "__fcmpl__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_FCMPG: // shouldn't appear in the final code
+ return "__fcmpg__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_DCMPL: // shouldn't appear in the final code
+ return "__dcmpl__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ case FUNCTION_DCMPG: // shouldn't appear in the final code
+ return "__dcmpg__(" +
+ wrapOperandString(lstOperands.get(0), true, indent) +
+ "," +
+ wrapOperandString(lstOperands.get(1), true, indent) +
+ ")";
+ }
+
+ if (functype <= FUNCTION_I2S) {
+ return "(" + ExprProcessor.getTypeName(types[functype - FUNCTION_I2L]) + ")" + wrapOperandString(lstOperands.get(0), true, indent);
+ }
+
+ // 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 String wrapOperandString(Exprent expr, boolean eq, int indent) {
+
+ 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);
+ }
+ }
+ }
+
+ String res = expr.toJava(indent);
+
+ if (parentheses) {
+ res = "(" + res + ")";
+ }
+
+ 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;
+ }
+}
+