diff options
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java new file mode 100644 index 000000000000..90ebe2f8218d --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java @@ -0,0 +1,853 @@ +/* + * 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().copy(); + arrtype.decArrayDim(); + + 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().copy(); + arrtype.decArrayDim(); + + 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())); + 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(new Exprent[]{econd})); + 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); + 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; + 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(new Exprent[]{ + stif.getHeadexprent().getCondition(), + ifas.getRight(), + elseas.getRight()})))); + 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(new Exprent[]{ + stif.getHeadexprent().getCondition(), + ifex.getValue(), + elseex.getValue()})), ifex.getRettype())); + 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; + } +} |