summaryrefslogtreecommitdiffstats
path: root/src/de/fernflower/modules/decompiler/exps/FunctionExprent.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/fernflower/modules/decompiler/exps/FunctionExprent.java')
-rw-r--r--src/de/fernflower/modules/decompiler/exps/FunctionExprent.java586
1 files changed, 586 insertions, 0 deletions
diff --git a/src/de/fernflower/modules/decompiler/exps/FunctionExprent.java b/src/de/fernflower/modules/decompiler/exps/FunctionExprent.java
new file mode 100644
index 0000000..230fc5b
--- /dev/null
+++ b/src/de/fernflower/modules/decompiler/exps/FunctionExprent.java
@@ -0,0 +1,586 @@
+/*
+ * Fernflower - The Analytical Java Decompiler
+ * http://www.reversed-java.com
+ *
+ * (C) 2008 - 2010, Stiver
+ *
+ * This software is NEITHER public domain NOR free software
+ * as per GNU License. See license.txt for more details.
+ *
+ * This software is distributed WITHOUT ANY WARRANTY; without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE.
+ */
+
+package de.fernflower.modules.decompiler.exps;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import de.fernflower.code.CodeConstants;
+import de.fernflower.modules.decompiler.ExprProcessor;
+import de.fernflower.modules.decompiler.vars.CheckTypesResult;
+import de.fernflower.struct.gen.VarType;
+import de.fernflower.util.InterpreterUtil;
+import de.fernflower.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) {
+ 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){
+ exprType = VarType.VARTYPE_BOOLEAN;
+ } else if(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:
+ if(type1.type == CodeConstants.TYPE_BOOLEAN &&
+ ((type1.convinfo & VarType.FALSEBOOLEAN) != 0 || type2.type != CodeConstants.TYPE_BOOLEAN)) {
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ }
+ if(type2.type == CodeConstants.TYPE_BOOLEAN &&
+ ((type2.convinfo & VarType.FALSEBOOLEAN) != 0 || type1.type != CodeConstants.TYPE_BOOLEAN)) {
+ result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);
+ }
+ break;
+ case FUNCTION_EQ:
+ case FUNCTION_NE:
+
+ if(type1.type == CodeConstants.TYPE_BOOLEAN) {
+
+ if(type2.isStrictSuperset(type1)) {
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ } else {
+ if(param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue()) {
+ if(param2.type != Exprent.EXPRENT_CONST || !((ConstExprent)param2).hasBooleanValue()) { // variable or not boolean constant
+ result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);
+ }
+ }
+ }
+ }
+
+ if(type2.type == CodeConstants.TYPE_BOOLEAN) {
+
+ if(type1.isStrictSuperset(type2)) {
+ result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);
+ } else {
+ if(param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue()) {
+ if(param1.type != Exprent.EXPRENT_CONST || !((ConstExprent)param1).hasBooleanValue()) { // variable or not boolean constant
+ 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!=null && o instanceof FunctionExprent) {
+ FunctionExprent fe = (FunctionExprent)o;
+
+ return functype==fe.getFunctype() &&
+ InterpreterUtil.equalLists(lstOperands, fe.getLstOperands()); // TODO: order of operands insignificant
+ }
+ return false;
+ }
+
+ 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);
+ }
+
+ 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 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;
+ }
+
+}
+