blob: 5643f228e0245ae12c0e756b6572f1818a1039f9 [file] [log] [blame]
/*
* 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;
}
}