diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java new file mode 100644 index 000000000000..5643f228e024 --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java @@ -0,0 +1,432 @@ +/* + * 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; + +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.*; +import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.gen.VarType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class SecondaryFunctionsHelper { + + private static final int[] funcsnot = new int[]{ + FunctionExprent.FUNCTION_NE, + FunctionExprent.FUNCTION_EQ, + FunctionExprent.FUNCTION_GE, + FunctionExprent.FUNCTION_LT, + FunctionExprent.FUNCTION_LE, + FunctionExprent.FUNCTION_GT, + FunctionExprent.FUNCTION_COR, + FunctionExprent.FUNCTION_CADD + }; + + private static final HashMap<Integer, Integer[]> mapNumComparisons = new HashMap<Integer, Integer[]>(); + + static { + mapNumComparisons.put(FunctionExprent.FUNCTION_EQ, + new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_GT}); + mapNumComparisons.put(FunctionExprent.FUNCTION_NE, + new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LE}); + mapNumComparisons.put(FunctionExprent.FUNCTION_GT, new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, null}); + mapNumComparisons.put(FunctionExprent.FUNCTION_GE, new Integer[]{null, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT}); + mapNumComparisons.put(FunctionExprent.FUNCTION_LT, new Integer[]{null, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE}); + mapNumComparisons.put(FunctionExprent.FUNCTION_LE, new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null}); + } + + + public static boolean identifySecondaryFunctions(Statement stat) { + + if (stat.getExprents() == null) { + // if(){;}else{...} -> if(!){...} + if (stat.type == Statement.TYPE_IF) { + IfStatement ifelsestat = (IfStatement)stat; + Statement ifstat = ifelsestat.getIfstat(); + + if (ifelsestat.iftype == IfStatement.IFTYPE_IFELSE && ifstat.getExprents() != null && + ifstat.getExprents().isEmpty() && (ifstat.getAllSuccessorEdges().isEmpty() || !ifstat.getAllSuccessorEdges().get(0).explicit)) { + + // move else to the if position + ifelsestat.getStats().removeWithKey(ifstat.id); + + ifelsestat.iftype = IfStatement.IFTYPE_IF; + ifelsestat.setIfstat(ifelsestat.getElsestat()); + ifelsestat.setElsestat(null); + + if (ifelsestat.getAllSuccessorEdges().isEmpty() && !ifstat.getAllSuccessorEdges().isEmpty()) { + StatEdge endedge = ifstat.getAllSuccessorEdges().get(0); + + ifstat.removeSuccessor(endedge); + endedge.setSource(ifelsestat); + if (endedge.closure != null) { + ifelsestat.getParent().addLabeledEdge(endedge); + } + ifelsestat.addSuccessor(endedge); + } + + ifelsestat.getFirst().removeSuccessor(ifelsestat.getIfEdge()); + + ifelsestat.setIfEdge(ifelsestat.getElseEdge()); + ifelsestat.setElseEdge(null); + + // negate head expression + ifelsestat.setNegated(!ifelsestat.isNegated()); + ifelsestat.getHeadexprentList().set(0, ((IfExprent)ifelsestat.getHeadexprent().copy()).negateIf()); + + return true; + } + } + } + + + boolean replaced = true; + while (replaced) { + replaced = false; + + List<Object> lstObjects = new ArrayList<Object>(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents()); + + for (int i = 0; i < lstObjects.size(); i++) { + Object obj = lstObjects.get(i); + + if (obj instanceof Statement) { + if (identifySecondaryFunctions((Statement)obj)) { + replaced = true; + break; + } + } + else if (obj instanceof Exprent) { + Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true); + if (retexpr != null) { + if (stat.getExprents() == null) { + // only head expressions can be replaced! + stat.replaceExprent((Exprent)obj, retexpr); + } + else { + stat.getExprents().set(i, retexpr); + } + replaced = true; + break; + } + } + } + } + + return false; + } + + + private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + + switch (fexpr.getFunctype()) { + case FunctionExprent.FUNCTION_BOOLNOT: + + Exprent retparam = propagateBoolNot(fexpr); + + if (retparam != null) { + return retparam; + } + + break; + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + case FunctionExprent.FUNCTION_GT: + case FunctionExprent.FUNCTION_GE: + case FunctionExprent.FUNCTION_LT: + case FunctionExprent.FUNCTION_LE: + Exprent expr1 = fexpr.getLstOperands().get(0); + Exprent expr2 = fexpr.getLstOperands().get(1); + + if (expr1.type == Exprent.EXPRENT_CONST) { + expr2 = expr1; + expr1 = fexpr.getLstOperands().get(1); + } + + if (expr1.type == Exprent.EXPRENT_FUNCTION && expr2.type == Exprent.EXPRENT_CONST) { + FunctionExprent funcexpr = (FunctionExprent)expr1; + ConstExprent cexpr = (ConstExprent)expr2; + + int functype = funcexpr.getFunctype(); + if (functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG || + functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG || + functype == FunctionExprent.FUNCTION_DCMPL) { + + int desttype = -1; + + Integer[] destcons = mapNumComparisons.get(fexpr.getFunctype()); + if (destcons != null) { + int index = cexpr.getIntValue() + 1; + if (index >= 0 && index <= 2) { + Integer destcon = destcons[index]; + if (destcon != null) { + desttype = destcon.intValue(); + } + } + } + + if (desttype >= 0) { + return new FunctionExprent(desttype, funcexpr.getLstOperands()); + } + } + } + } + } + + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Exprent expr : exprent.getAllExprents()) { + Exprent retexpr = identifySecondaryFunctions(expr, false); + if (retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + break; + } + } + } + + switch (exprent.type) { + case Exprent.EXPRENT_FUNCTION: + FunctionExprent fexpr = (FunctionExprent)exprent; + List<Exprent> lstOperands = fexpr.getLstOperands(); + + switch (fexpr.getFunctype()) { + case FunctionExprent.FUNCTION_XOR: + for (int i = 0; i < 2; i++) { + Exprent operand = lstOperands.get(i); + VarType operandtype = operand.getExprType(); + + if (operand.type == Exprent.EXPRENT_CONST && + operandtype.type != CodeConstants.TYPE_BOOLEAN) { + ConstExprent cexpr = (ConstExprent)operand; + long val; + if (operandtype.type == CodeConstants.TYPE_LONG) { + val = ((Long)cexpr.getValue()).longValue(); + } + else { + val = ((Integer)cexpr.getValue()).intValue(); + } + + if (val == -1) { + List<Exprent> lstBitNotOperand = new ArrayList<Exprent>(); + lstBitNotOperand.add(lstOperands.get(1 - i)); + return new FunctionExprent(FunctionExprent.FUNCTION_BITNOT, lstBitNotOperand); + } + } + } + break; + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + if (lstOperands.get(0).getExprType().type == CodeConstants.TYPE_BOOLEAN && + lstOperands.get(1).getExprType().type == CodeConstants.TYPE_BOOLEAN) { + for (int i = 0; i < 2; i++) { + if (lstOperands.get(i).type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr = (ConstExprent)lstOperands.get(i); + int val = ((Integer)cexpr.getValue()).intValue(); + + if ((fexpr.getFunctype() == FunctionExprent.FUNCTION_EQ && val == 1) || + (fexpr.getFunctype() == FunctionExprent.FUNCTION_NE && val == 0)) { + return lstOperands.get(1 - i); + } + else { + List<Exprent> lstNotOperand = new ArrayList<Exprent>(); + lstNotOperand.add(lstOperands.get(1 - i)); + return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, lstNotOperand); + } + } + } + } + break; + case FunctionExprent.FUNCTION_BOOLNOT: + if (lstOperands.get(0).type == Exprent.EXPRENT_CONST) { + int val = ((ConstExprent)lstOperands.get(0)).getIntValue(); + if (val == 0) { + return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(1)); + } + else { + return new ConstExprent(VarType.VARTYPE_BOOLEAN, new Integer(0)); + } + } + break; + case FunctionExprent.FUNCTION_IIF: + Exprent expr1 = lstOperands.get(1); + Exprent expr2 = lstOperands.get(2); + + if (expr1.type == Exprent.EXPRENT_CONST && expr2.type == Exprent.EXPRENT_CONST) { + ConstExprent cexpr1 = (ConstExprent)expr1; + ConstExprent cexpr2 = (ConstExprent)expr2; + + if (cexpr1.getExprType().type == CodeConstants.TYPE_BOOLEAN && + cexpr2.getExprType().type == CodeConstants.TYPE_BOOLEAN) { + + if (cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) { + return new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, Arrays.asList(new Exprent[]{lstOperands.get(0)})); + } + else if (cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) { + return lstOperands.get(0); + } + } + } + break; + case FunctionExprent.FUNCTION_LCMP: + case FunctionExprent.FUNCTION_FCMPL: + case FunctionExprent.FUNCTION_FCMPG: + case FunctionExprent.FUNCTION_DCMPL: + case FunctionExprent.FUNCTION_DCMPG: + int var = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER); + VarType type = lstOperands.get(0).getExprType(); + VarProcessor processor = (VarProcessor)DecompilerContext.getProperty(DecompilerContext.CURRENT_VAR_PROCESSOR); + + FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{ + new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new Exprent[]{new VarExprent(var, type, processor), + ConstExprent.getZeroConstant(type.type)})), + new ConstExprent(VarType.VARTYPE_INT, new Integer(-1)), + new ConstExprent(VarType.VARTYPE_INT, new Integer(1))})); + + FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(new Exprent[]{ + new AssignmentExprent(new VarExprent(var, type, processor), new FunctionExprent(FunctionExprent.FUNCTION_SUB, + Arrays.asList( + new Exprent[]{lstOperands.get(0), + lstOperands.get(1)}))), + ConstExprent.getZeroConstant(type.type)})); + + processor.setVarType(new VarVersionPaar(var, 0), type); + + return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(new Exprent[]{ + head, new ConstExprent(VarType.VARTYPE_INT, new Integer(0)), iff})); + } + break; + case Exprent.EXPRENT_ASSIGNMENT: // check for conditional assignment + AssignmentExprent asexpr = (AssignmentExprent)exprent; + Exprent right = asexpr.getRight(); + Exprent left = asexpr.getLeft(); + + if (right.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent func = (FunctionExprent)right; + + VarType midlayer = null; + if (func.getFunctype() >= FunctionExprent.FUNCTION_I2L && + func.getFunctype() <= FunctionExprent.FUNCTION_I2S) { + right = func.getLstOperands().get(0); + midlayer = func.getSimpleCastType(); + if (right.type == Exprent.EXPRENT_FUNCTION) { + func = (FunctionExprent)right; + } + else { + return null; + } + } + + List<Exprent> lstFuncOperands = func.getLstOperands(); + + Exprent cond = null; + + switch (func.getFunctype()) { + case FunctionExprent.FUNCTION_ADD: + case FunctionExprent.FUNCTION_AND: + case FunctionExprent.FUNCTION_OR: + case FunctionExprent.FUNCTION_XOR: + if (left.equals(lstFuncOperands.get(1))) { + cond = lstFuncOperands.get(0); + break; + } + case FunctionExprent.FUNCTION_SUB: + case FunctionExprent.FUNCTION_MUL: + case FunctionExprent.FUNCTION_DIV: + case FunctionExprent.FUNCTION_REM: + case FunctionExprent.FUNCTION_SHL: + case FunctionExprent.FUNCTION_SHR: + case FunctionExprent.FUNCTION_USHR: + if (left.equals(lstFuncOperands.get(0))) { + cond = lstFuncOperands.get(1); + } + } + + if (cond != null && (midlayer == null || midlayer.equals(cond.getExprType()))) { + asexpr.setRight(cond); + asexpr.setCondtype(func.getFunctype()); + } + } + break; + case Exprent.EXPRENT_INVOCATION: + if (!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once). + Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent); + if (!exprent.equals(retexpr)) { + return retexpr; + } + } + } + + return null; + } + + public static Exprent propagateBoolNot(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fexpr = (FunctionExprent)exprent; + + if (fexpr.getFunctype() == FunctionExprent.FUNCTION_BOOLNOT) { + + Exprent param = fexpr.getLstOperands().get(0); + + if (param.type == Exprent.EXPRENT_FUNCTION) { + FunctionExprent fparam = (FunctionExprent)param; + + int ftype = fparam.getFunctype(); + switch (ftype) { + case FunctionExprent.FUNCTION_BOOLNOT: + Exprent newexpr = fparam.getLstOperands().get(0); + Exprent retexpr = propagateBoolNot(newexpr); + return retexpr == null ? newexpr : retexpr; + case FunctionExprent.FUNCTION_CADD: + case FunctionExprent.FUNCTION_COR: + List<Exprent> operands = fparam.getLstOperands(); + for (int i = 0; i < operands.size(); i++) { + Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOLNOT, + Arrays.asList(new Exprent[]{operands.get(i)})); + + Exprent retparam = propagateBoolNot(newparam); + operands.set(i, retparam == null ? newparam : retparam); + } + case FunctionExprent.FUNCTION_EQ: + case FunctionExprent.FUNCTION_NE: + case FunctionExprent.FUNCTION_LT: + case FunctionExprent.FUNCTION_GE: + case FunctionExprent.FUNCTION_GT: + case FunctionExprent.FUNCTION_LE: + fparam.setFunctype(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]); + return fparam; + } + } + } + } + + return null; + } +} |