fernflower/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java

856 lines
29 KiB
Java

/*
* 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.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
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.VarVersionPaar;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
import java.util.*;
import java.util.Map.Entry;
public class SimplifyExprentsHelper {
private boolean firstInvocation;
public SimplifyExprentsHelper(boolean firstInvocation) {
this.firstInvocation = firstInvocation;
}
public boolean simplifyStackVarsStatement(Statement stat, HashSet<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) {
boolean res = false;
if (stat.getExprents() == null) {
while (true) {
boolean changed = false;
for (Statement st : stat.getStats()) {
res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl);
// collapse composed if's
if (changed = IfHelper.mergeIfs(st, setReorderedIfs)) {
break;
}
// collapse iff ?: statement
if (changed = buildIff(st, ssa)) {
break;
}
}
res |= changed;
if (!changed) {
break;
}
}
}
else {
res |= simplifyStackVarsExprents(stat.getExprents(), cl);
}
return res;
}
private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) {
boolean res = false;
int index = 0;
while (index < list.size()) {
Exprent current = list.get(index);
Exprent ret = isSimpleConstructorInvocation(current);
if (ret != null) {
list.set(index, ret);
res = true;
continue;
}
// lambda expression (Java 8)
ret = isLambda(current, cl);
if (ret != null) {
list.set(index, ret);
res = true;
continue;
}
// remove monitor exit
if (isMonitorExit(current)) {
list.remove(index);
res = true;
continue;
}
// trivial assignment of a stack variable
if (isTrivialStackAssignment(current)) {
list.remove(index);
res = true;
continue;
}
if (index == list.size() - 1) {
break;
}
Exprent next = list.get(index + 1);
// constructor invocation
if (isConstructorInvocationRemote(list, index)) {
list.remove(index);
res = true;
continue;
}
// remove getClass() invocation, which is part of a qualified new
if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GET_CLASS_NEW)) {
if (isQualifiedNewGetClass(current, next)) {
list.remove(index);
res = true;
continue;
}
}
// direct initialization of an array
int arrcount = isArrayInitializer(list, index);
if (arrcount > 0) {
for (int i = 0; i < arrcount; i++) {
list.remove(index + 1);
}
res = true;
continue;
}
// add array initializer expression
if (addArrayInitializer(current, next)) {
list.remove(index + 1);
res = true;
continue;
}
// integer ++expr and --expr (except for vars!)
Exprent func = isPPIorMMI(current);
if (func != null) {
list.set(index, func);
res = true;
continue;
}
// expr++ and expr--
if (isIPPorIMM(current, next)) {
list.remove(index + 1);
res = true;
continue;
}
// assignment on stack
if (isStackAssignement(current, next)) {
list.remove(index + 1);
res = true;
continue;
}
if (!firstInvocation && isStackAssignement2(current, next)) {
list.remove(index + 1);
res = true;
continue;
}
index++;
}
return res;
}
private static boolean addArrayInitializer(Exprent first, Exprent second) {
if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent as = (AssignmentExprent)first;
if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {
NewExprent newex = (NewExprent)as.getRight();
if (!newex.getLstArrayElements().isEmpty()) {
VarExprent arrvar = (VarExprent)as.getLeft();
if (second.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent aas = (AssignmentExprent)second;
if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {
ArrayExprent arrex = (ArrayExprent)aas.getLeft();
if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray())
&& arrex.getIndex().type == Exprent.EXPRENT_CONST) {
int constvalue = ((ConstExprent)arrex.getIndex()).getIntValue();
if (constvalue < newex.getLstArrayElements().size()) {
Exprent init = newex.getLstArrayElements().get(constvalue);
if (init.type == Exprent.EXPRENT_CONST) {
ConstExprent cinit = (ConstExprent)init;
VarType arrtype = newex.getNewType().decreaseArrayDim();
ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype);
if (cinit.equals(defaultval)) {
Exprent tempexpr = aas.getRight();
if (!tempexpr.containsExprent(arrvar)) {
newex.getLstArrayElements().set(constvalue, tempexpr);
if (tempexpr.type == Exprent.EXPRENT_NEW) {
NewExprent tempnewex = (NewExprent)tempexpr;
int dims = newex.getNewType().arrayDim;
if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) {
tempnewex.setDirectArrayInit(true);
}
}
return true;
}
}
}
}
}
}
}
}
}
}
return false;
}
private static int isArrayInitializer(List<Exprent> list, int index) {
Exprent current = list.get(index);
if (current.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent as = (AssignmentExprent)current;
if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {
NewExprent newex = (NewExprent)as.getRight();
if (newex.getExprType().arrayDim > 0 && newex.getLstDims().size() == 1 && newex.getLstArrayElements().isEmpty() &&
newex.getLstDims().get(0).type == Exprent.EXPRENT_CONST) {
int size = ((Integer)((ConstExprent)newex.getLstDims().get(0)).getValue()).intValue();
if (size == 0) {
return 0;
}
VarExprent arrvar = (VarExprent)as.getLeft();
HashMap<Integer, Exprent> mapInit = new HashMap<Integer, Exprent>();
int i = 1;
while (index + i < list.size() && i <= size) {
boolean found = false;
Exprent expr = list.get(index + i);
if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent aas = (AssignmentExprent)expr;
if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {
ArrayExprent arrex = (ArrayExprent)aas.getLeft();
if (arrex.getArray().type == Exprent.EXPRENT_VAR && arrvar.equals(arrex.getArray())
&& arrex.getIndex().type == Exprent.EXPRENT_CONST) {
int constvalue = ((ConstExprent)arrex.getIndex())
.getIntValue(); // TODO: check for a number type. Failure extremely improbable, but nevertheless...
if (constvalue < size && !mapInit.containsKey(constvalue)) {
if (!aas.getRight().containsExprent(arrvar)) {
mapInit.put(constvalue, aas.getRight());
found = true;
}
}
}
}
}
if (!found) {
break;
}
i++;
}
double fraction = ((double)mapInit.size()) / size;
if ((arrvar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) ||
(size > 7 && fraction >= 0.7)) {
List<Exprent> lstRet = new ArrayList<Exprent>();
VarType arrtype = newex.getNewType().decreaseArrayDim();
ConstExprent defaultval = ExprProcessor.getDefaultArrayValue(arrtype);
for (int j = 0; j < size; j++) {
lstRet.add(defaultval.copy());
}
int dims = newex.getNewType().arrayDim;
for (Entry<Integer, Exprent> ent : mapInit.entrySet()) {
Exprent tempexpr = ent.getValue();
lstRet.set(ent.getKey(), tempexpr);
if (tempexpr.type == Exprent.EXPRENT_NEW) {
NewExprent tempnewex = (NewExprent)tempexpr;
if (dims > 1 && !tempnewex.getLstArrayElements().isEmpty()) {
tempnewex.setDirectArrayInit(true);
}
}
}
newex.setLstArrayElements(lstRet);
return mapInit.size();
}
}
}
}
return 0;
}
private static boolean isTrivialStackAssignment(Exprent first) {
if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asf = (AssignmentExprent)first;
if (asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) {
VarExprent varleft = (VarExprent)asf.getLeft();
VarExprent varright = (VarExprent)asf.getRight();
if (varleft.getIndex() == varright.getIndex() && varleft.isStack() &&
varright.isStack()) {
return true;
}
}
}
return false;
}
private static boolean isStackAssignement2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation
if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asf = (AssignmentExprent)first;
AssignmentExprent ass = (AssignmentExprent)second;
if (asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR &&
asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) {
if (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) {
asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight(), ass.bytecode));
return true;
}
}
}
return false;
}
private static boolean isStackAssignement(Exprent first, Exprent second) {
if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent asf = (AssignmentExprent)first;
AssignmentExprent ass = (AssignmentExprent)second;
while (true) {
if (asf.getRight().equals(ass.getRight())) {
if ((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) &&
(ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) {
if (!ass.getLeft().containsExprent(asf.getLeft())) {
asf.setRight(ass);
return true;
}
}
}
if (asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) {
asf = (AssignmentExprent)asf.getRight();
}
else {
break;
}
}
}
return false;
}
private static Exprent isPPIorMMI(Exprent first) {
if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent as = (AssignmentExprent)first;
if (as.getRight().type == Exprent.EXPRENT_FUNCTION) {
FunctionExprent func = (FunctionExprent)as.getRight();
if (func.getFuncType() == FunctionExprent.FUNCTION_ADD ||
func.getFuncType() == FunctionExprent.FUNCTION_SUB) {
Exprent econd = func.getLstOperands().get(0);
Exprent econst = func.getLstOperands().get(1);
if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST &&
func.getFuncType() == FunctionExprent.FUNCTION_ADD) {
econd = econst;
econst = func.getLstOperands().get(0);
}
if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {
Exprent left = as.getLeft();
if (left.type != Exprent.EXPRENT_VAR && left.equals(econd)) {
FunctionExprent ret = new FunctionExprent(
func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI,
Arrays.asList(econd), func.bytecode);
ret.setImplicitType(VarType.VARTYPE_INT);
return ret;
}
}
}
}
}
return null;
}
private static boolean isIPPorIMM(Exprent first, Exprent second) {
if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) {
AssignmentExprent as = (AssignmentExprent)first;
FunctionExprent in = (FunctionExprent)second;
if ((in.getFuncType() == FunctionExprent.FUNCTION_MMI || in.getFuncType() == FunctionExprent.FUNCTION_PPI) &&
in.getLstOperands().get(0).equals(as.getRight())) {
if (in.getFuncType() == FunctionExprent.FUNCTION_MMI) {
in.setFuncType(FunctionExprent.FUNCTION_IMM);
}
else {
in.setFuncType(FunctionExprent.FUNCTION_IPP);
}
as.setRight(in);
return true;
}
}
return false;
}
private static boolean isMonitorExit(Exprent first) {
if (first.type == Exprent.EXPRENT_MONITOR) {
MonitorExprent monexpr = (MonitorExprent)first;
if (monexpr.getMonType() == MonitorExprent.MONITOR_EXIT && monexpr.getValue().type == Exprent.EXPRENT_VAR
&& !((VarExprent)monexpr.getValue()).isStack()) {
return true;
}
}
return false;
}
private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) {
if (first.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invexpr = (InvocationExprent)first;
if (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR && invexpr.getName().equals("getClass") &&
invexpr.getStringDescriptor().equals("()Ljava/lang/Class;")) {
List<Exprent> lstExprents = second.getAllExprents();
lstExprents.add(second);
for (Exprent expr : lstExprents) {
if (expr.type == Exprent.EXPRENT_NEW) {
NewExprent nexpr = (NewExprent)expr;
if (nexpr.getConstructor() != null && !nexpr.getConstructor().getLstParameters().isEmpty() &&
nexpr.getConstructor().getLstParameters().get(0).equals(invexpr.getInstance())) {
String classname = nexpr.getNewType().value;
ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
if (node != null && node.type != ClassNode.CLASS_ROOT) {
return true;
}
}
}
}
}
}
return false;
}
// private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) {
//
// Exprent current = list.get(index);
//
// if(current.type == Exprent.EXPRENT_ASSIGNMENT) {
// AssignmentExprent as = (AssignmentExprent)current;
//
// if(as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) {
//
// NewExprent newexpr = (NewExprent)as.getRight();
// VarType newtype = newexpr.getNewType();
// VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft());
//
// if(newtype.type == CodeConstants.TYPE_OBJECT && newtype.arrayDim == 0 &&
// newexpr.getConstructor() == null) {
//
// Set<VarVersionPaar> setChangedVars = new HashSet<VarVersionPaar>();
//
// for(int i = index + 1; i < list.size(); i++) {
// Exprent remote = list.get(i);
//
// if(remote.type == Exprent.EXPRENT_INVOCATION) {
// InvocationExprent in = (InvocationExprent)remote;
//
// if(in.getFuncType() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_VAR
// && as.getLeft().equals(in.getInstance())) {
//
// Set<VarVersionPaar> setVars = remote.getAllVariables();
// setVars.remove(leftPaar);
// setVars.retainAll(setChangedVars);
//
// if(setVars.isEmpty()) {
//
// newexpr.setConstructor(in);
// in.setInstance(null);
//
// if(!setChangedVars.isEmpty()) { // some exprents inbetween
// list.add(index+1, as.copy());
// list.remove(i+1);
// } else {
// list.set(i, as.copy());
// }
//
// return true;
// }
// }
// }
//
// boolean isTempAssignment = false;
//
// if(remote.type == Exprent.EXPRENT_ASSIGNMENT) { // ugly solution
// AssignmentExprent asremote = (AssignmentExprent)remote;
// if(asremote.getLeft().type == Exprent.EXPRENT_VAR &&
// asremote.getRight().type == Exprent.EXPRENT_VAR) {
// setChangedVars.add(new VarVersionPaar((VarExprent)asremote.getLeft()));
// isTempAssignment = true;
// }
//
// // FIXME: needs to be rewritten
// // propagate (var = new X) forward to the <init> invokation and then reduce
//
//// if(asremote.getLeft().type == Exprent.EXPRENT_VAR) {
//// List<Exprent> lstRightExprents = asremote.getRight().getAllExprents(true);
//// lstRightExprents.add(asremote.getRight());
////
//// Set<VarVersionPaar> setTempChangedVars = new HashSet<VarVersionPaar>();
//// boolean isTemp = true;
////
//// for(Exprent expr : lstRightExprents) {
//// if(expr.type != Exprent.EXPRENT_VAR && expr.type != Exprent.EXPRENT_FIELD) {
//// isTemp = false;
//// break;
//// } else if(expr.type == Exprent.EXPRENT_VAR) {
//// setTempChangedVars.add(new VarVersionPaar((VarExprent)expr));
//// }
//// }
////
//// if(isTemp) {
//// setChangedVars.addAll(setTempChangedVars);
//// isTempAssignment = true;
//// }
//// }
//// } else if(remote.type == Exprent.EXPRENT_FUNCTION) {
//// FunctionExprent fexpr = (FunctionExprent)remote;
//// if(fexpr.getFuncType() == FunctionExprent.FUNCTION_IPP || fexpr.getFuncType() == FunctionExprent.FUNCTION_IMM
//// || fexpr.getFuncType() == FunctionExprent.FUNCTION_PPI || fexpr.getFuncType() == FunctionExprent.FUNCTION_MMI) {
//// if(fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) {
//// setChangedVars.add(new VarVersionPaar((VarExprent)fexpr.getLstOperands().get(0)));
//// isTempAssignment = true;
//// }
//// }
// }
//
// if(!isTempAssignment) {
// Set<VarVersionPaar> setVars = remote.getAllVariables();
// if(setVars.contains(leftPaar)) {
// return false;
// } else {
// setChangedVars.addAll(setVars);
// }
// }
// }
// }
// }
// }
//
// return false;
// }
// propagate (var = new X) forward to the <init> invokation
private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) {
Exprent current = list.get(index);
if (current.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent as = (AssignmentExprent)current;
if (as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) {
NewExprent newexpr = (NewExprent)as.getRight();
VarType newtype = newexpr.getNewType();
VarVersionPaar leftPaar = new VarVersionPaar((VarExprent)as.getLeft());
if (newtype.type == CodeConstants.TYPE_OBJECT && newtype.arrayDim == 0 && newexpr.getConstructor() == null) {
for (int i = index + 1; i < list.size(); i++) {
Exprent remote = list.get(i);
// <init> invocation
if (remote.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent in = (InvocationExprent)remote;
if (in.getFunctype() == InvocationExprent.TYP_INIT &&
in.getInstance().type == Exprent.EXPRENT_VAR &&
as.getLeft().equals(in.getInstance())) {
newexpr.setConstructor(in);
in.setInstance(null);
list.set(i, as.copy());
return true;
}
}
// check for variable in use
Set<VarVersionPaar> setVars = remote.getAllVariables();
if (setVars.contains(leftPaar)) { // variable used somewhere in between -> exit, need a better reduced code
return false;
}
}
}
}
}
return false;
}
private static Exprent isLambda(Exprent exprent, StructClass cl) {
List<Exprent> lst = exprent.getAllExprents();
for (Exprent expr : lst) {
Exprent ret = isLambda(expr, cl);
if (ret != null) {
exprent.replaceExprent(expr, ret);
}
}
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent in = (InvocationExprent)exprent;
if (in.getInvocationTyp() == InvocationExprent.INVOKE_DYNAMIC) {
String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix();
ClassNode lambda_class = DecompilerContext.getClassProcessor().getMapRootClasses().get(lambda_class_name);
if (lambda_class != null) { // real lambda class found, replace invocation with an anonymous class
NewExprent newexp = new NewExprent(new VarType(lambda_class_name, true), null, 0, in.bytecode);
newexp.setConstructor(in);
// note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invokation
// lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);)
// in this case instance will hold the corresponding object
return newexp;
}
}
}
return null;
}
private static Exprent isSimpleConstructorInvocation(Exprent exprent) {
List<Exprent> lst = exprent.getAllExprents();
for (Exprent expr : lst) {
Exprent ret = isSimpleConstructorInvocation(expr);
if (ret != null) {
exprent.replaceExprent(expr, ret);
}
}
if (exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent in = (InvocationExprent)exprent;
if (in.getFunctype() == InvocationExprent.TYP_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) {
NewExprent newexp = (NewExprent)in.getInstance();
newexp.setConstructor(in);
in.setInstance(null);
return newexp;
}
}
return null;
}
private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) {
if (stat.type == Statement.TYPE_IF && stat.getExprents() == null) {
IfStatement stif = (IfStatement)stat;
Exprent ifheadexpr = stif.getHeadexprent();
Set<Integer> ifheadexpr_bytecode = (ifheadexpr == null ? null : ifheadexpr.bytecode);
if (stif.iftype == IfStatement.IFTYPE_IFELSE) {
Statement ifstat = stif.getIfstat();
Statement elsestat = stif.getElsestat();
if (ifstat.getExprents() != null && ifstat.getExprents().size() == 1
&& elsestat.getExprents() != null && elsestat.getExprents().size() == 1
&& ifstat.getAllSuccessorEdges().size() == 1 && elsestat.getAllSuccessorEdges().size() == 1
&& ifstat.getAllSuccessorEdges().get(0).getDestination() == elsestat.getAllSuccessorEdges().get(0).getDestination()) {
Exprent ifexpr = ifstat.getExprents().get(0);
Exprent elseexpr = elsestat.getExprents().get(0);
if (ifexpr.type == Exprent.EXPRENT_ASSIGNMENT && elseexpr.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent ifas = (AssignmentExprent)ifexpr;
AssignmentExprent elseas = (AssignmentExprent)elseexpr;
if (ifas.getLeft().type == Exprent.EXPRENT_VAR && elseas.getLeft().type == Exprent.EXPRENT_VAR) {
VarExprent ifvar = (VarExprent)ifas.getLeft();
VarExprent elsevar = (VarExprent)elseas.getLeft();
if (ifvar.getIndex() == elsevar.getIndex() && ifvar.isStack()) { // ifvar.getIndex() >= VarExprent.STACK_BASE) {
boolean found = false;
for (Entry<VarVersionPaar, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {
if (ent.getKey().var == ifvar.getIndex()) {
if (ent.getValue().contains(ifvar.getVersion()) && ent.getValue().contains(elsevar.getVersion())) {
found = true;
break;
}
}
}
if (found) {
List<Exprent> data = new ArrayList<Exprent>();
data.addAll(stif.getFirst().getExprents());
data.add(new AssignmentExprent(ifvar, new FunctionExprent(FunctionExprent.FUNCTION_IIF,
Arrays.asList(
stif.getHeadexprent().getCondition(),
ifas.getRight(),
elseas.getRight()), ifheadexpr_bytecode), ifheadexpr_bytecode));
stif.setExprents(data);
if (stif.getAllSuccessorEdges().isEmpty()) {
StatEdge ifedge = ifstat.getAllSuccessorEdges().get(0);
StatEdge edge = new StatEdge(ifedge.getType(), stif, ifedge.getDestination());
stif.addSuccessor(edge);
if (ifedge.closure != null) {
ifedge.closure.addLabeledEdge(edge);
}
}
SequenceHelper.destroyAndFlattenStatement(stif);
return true;
}
}
}
}
else if (ifexpr.type == Exprent.EXPRENT_EXIT && elseexpr.type == Exprent.EXPRENT_EXIT) {
ExitExprent ifex = (ExitExprent)ifexpr;
ExitExprent elseex = (ExitExprent)elseexpr;
if (ifex.getExitType() == elseex.getExitType() && ifex.getValue() != null && elseex.getValue() != null &&
ifex.getExitType() == ExitExprent.EXIT_RETURN) {
// throw is dangerous, because of implicit casting to a common superclass
// e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work
if (ifex.getExitType() == ExitExprent.EXIT_THROW &&
!ifex.getValue().getExprType().equals(elseex.getValue().getExprType())) { // note: getExprType unreliable at this point!
return false;
}
List<Exprent> data = new ArrayList<Exprent>();
data.addAll(stif.getFirst().getExprents());
data.add(new ExitExprent(ifex.getExitType(), new FunctionExprent(FunctionExprent.FUNCTION_IIF,
Arrays.asList(
stif.getHeadexprent().getCondition(),
ifex.getValue(),
elseex.getValue()), ifheadexpr_bytecode), ifex.getRetType(), ifheadexpr_bytecode));
stif.setExprents(data);
StatEdge retedge = ifstat.getAllSuccessorEdges().get(0);
stif.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, stif, retedge.getDestination(),
retedge.closure == stif ? stif.getParent() : retedge.closure));
SequenceHelper.destroyAndFlattenStatement(stif);
return true;
}
}
}
}
}
return false;
}
}