1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.java.decompiler.modules.decompiler;
4 import org.jetbrains.java.decompiler.code.CodeConstants;
5 import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
6 import org.jetbrains.java.decompiler.main.DecompilerContext;
7 import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
8 import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
9 import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
10 import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
11 import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
12 import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
13 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
14 import org.jetbrains.java.decompiler.struct.StructClass;
15 import org.jetbrains.java.decompiler.struct.gen.VarType;
16 import org.jetbrains.java.decompiler.struct.match.MatchEngine;
17 import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
18 import org.jetbrains.java.decompiler.util.InterpreterUtil;
21 import java.util.Map.Entry;
23 public class SimplifyExprentsHelper {
24 @SuppressWarnings("SpellCheckingInspection") private static final MatchEngine class14Builder = new MatchEngine(
25 "statement type:if iftype:if exprsize:-1\n" +
26 " exprent position:head type:if\n" +
27 " exprent type:function functype:eq\n" +
28 " exprent type:field name:$fieldname$\n" +
29 " exprent type:constant consttype:null\n" +
30 " statement type:basicblock\n" +
31 " exprent position:-1 type:assignment ret:$assignfield$\n" +
32 " exprent type:var index:$var$\n" +
33 " exprent type:field name:$fieldname$\n" +
34 " statement type:sequence statsize:2\n" +
35 " statement type:trycatch\n" +
36 " statement type:basicblock exprsize:1\n" +
37 " exprent type:assignment\n" +
38 " exprent type:var index:$var$\n" +
39 " exprent type:invocation invclass:java/lang/Class signature:forName(Ljava/lang/String;)Ljava/lang/Class;\n" +
40 " exprent position:0 type:constant consttype:string constvalue:$classname$\n" +
41 " statement type:basicblock exprsize:1\n" +
42 " exprent type:exit exittype:throw\n" +
43 " statement type:basicblock exprsize:1\n" +
44 " exprent type:assignment\n" +
45 " exprent type:field name:$fieldname$ ret:$field$\n" +
46 " exprent type:var index:$var$");
48 private final boolean firstInvocation;
50 public SimplifyExprentsHelper(boolean firstInvocation) {
51 this.firstInvocation = firstInvocation;
54 public boolean simplifyStackVarsStatement(Statement stat, Set<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) {
57 List<Exprent> expressions = stat.getExprents();
58 if (expressions == null) {
59 boolean processClass14 = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4);
62 boolean changed = false;
64 for (Statement st : stat.getStats()) {
65 res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl);
67 changed = IfHelper.mergeIfs(st, setReorderedIfs) || // collapse composed if's
68 buildIff(st, ssa) || // collapse iff ?: statement
69 processClass14 && collapseInlinedClass14(st); // collapse inlined .class property in version 1.4 and before
84 res = simplifyStackVarsExprents(expressions, cl);
90 private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) {
94 while (index < list.size()) {
95 Exprent current = list.get(index);
97 Exprent ret = isSimpleConstructorInvocation(current);
104 // lambda expression (Java 8)
105 ret = isLambda(current, cl);
107 list.set(index, ret);
112 // remove monitor exit
113 if (isMonitorExit(current)) {
119 // trivial assignment of a stack variable
120 if (isTrivialStackAssignment(current)) {
126 if (index == list.size() - 1) {
130 Exprent next = list.get(index + 1);
132 // constructor invocation
133 if (isConstructorInvocationRemote(list, index)) {
139 // remove getClass() invocation, which is part of a qualified new
140 if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GET_CLASS_NEW)) {
141 if (isQualifiedNewGetClass(current, next)) {
148 // direct initialization of an array
149 int arrCount = isArrayInitializer(list, index);
151 for (int i = 0; i < arrCount; i++) {
152 list.remove(index + 1);
158 // add array initializer expression
159 if (addArrayInitializer(current, next)) {
160 list.remove(index + 1);
165 // integer ++expr and --expr (except for vars!)
166 Exprent func = isPPIorMMI(current);
168 list.set(index, func);
174 if (isIPPorIMM(current, next) || isIPPorIMM2(current, next)) {
175 list.remove(index + 1);
180 // assignment on stack
181 if (isStackAssignment(current, next)) {
182 list.remove(index + 1);
187 if (!firstInvocation && isStackAssignment2(current, next)) {
188 list.remove(index + 1);
199 private static boolean addArrayInitializer(Exprent first, Exprent second) {
200 if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
201 AssignmentExprent as = (AssignmentExprent)first;
203 if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {
204 NewExprent newExpr = (NewExprent)as.getRight();
206 if (!newExpr.getLstArrayElements().isEmpty()) {
207 VarExprent arrVar = (VarExprent)as.getLeft();
209 if (second.type == Exprent.EXPRENT_ASSIGNMENT) {
210 AssignmentExprent aas = (AssignmentExprent)second;
211 if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {
212 ArrayExprent arrExpr = (ArrayExprent)aas.getLeft();
213 if (arrExpr.getArray().type == Exprent.EXPRENT_VAR &&
214 arrVar.equals(arrExpr.getArray()) &&
215 arrExpr.getIndex().type == Exprent.EXPRENT_CONST) {
216 int constValue = ((ConstExprent)arrExpr.getIndex()).getIntValue();
218 if (constValue < newExpr.getLstArrayElements().size()) {
219 Exprent init = newExpr.getLstArrayElements().get(constValue);
220 if (init.type == Exprent.EXPRENT_CONST) {
221 ConstExprent cinit = (ConstExprent)init;
222 VarType arrType = newExpr.getNewType().decreaseArrayDim();
223 ConstExprent defaultVal = ExprProcessor.getDefaultArrayValue(arrType);
225 if (cinit.equals(defaultVal)) {
226 Exprent tempExpr = aas.getRight();
228 if (!tempExpr.containsExprent(arrVar)) {
229 newExpr.getLstArrayElements().set(constValue, tempExpr);
231 if (tempExpr.type == Exprent.EXPRENT_NEW) {
232 NewExprent tempNewExpr = (NewExprent)tempExpr;
233 int dims = newExpr.getNewType().arrayDim;
234 if (dims > 1 && !tempNewExpr.getLstArrayElements().isEmpty()) {
235 tempNewExpr.setDirectArrayInit(true);
254 private static int isArrayInitializer(List<Exprent> list, int index) {
255 Exprent current = list.get(index);
256 if (current.type == Exprent.EXPRENT_ASSIGNMENT) {
257 AssignmentExprent as = (AssignmentExprent)current;
259 if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {
260 NewExprent newExpr = (NewExprent)as.getRight();
262 if (newExpr.getExprType().arrayDim > 0 && newExpr.getLstDims().size() == 1 && newExpr.getLstArrayElements().isEmpty() &&
263 newExpr.getLstDims().get(0).type == Exprent.EXPRENT_CONST) {
265 int size = (Integer)((ConstExprent)newExpr.getLstDims().get(0)).getValue();
270 VarExprent arrVar = (VarExprent)as.getLeft();
271 Map<Integer, Exprent> mapInit = new HashMap<>();
274 while (index + i < list.size() && i <= size) {
275 boolean found = false;
277 Exprent expr = list.get(index + i);
278 if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {
279 AssignmentExprent aas = (AssignmentExprent)expr;
280 if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {
281 ArrayExprent arrExpr = (ArrayExprent)aas.getLeft();
282 if (arrExpr.getArray().type == Exprent.EXPRENT_VAR && arrVar.equals(arrExpr.getArray()) &&
283 arrExpr.getIndex().type == Exprent.EXPRENT_CONST) {
284 // TODO: check for a number type. Failure extremely improbable, but nevertheless...
285 int constValue = ((ConstExprent)arrExpr.getIndex()).getIntValue();
286 if (constValue < size && !mapInit.containsKey(constValue)) {
287 if (!aas.getRight().containsExprent(arrVar)) {
288 mapInit.put(constValue, aas.getRight());
303 double fraction = ((double)mapInit.size()) / size;
305 if ((arrVar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || (size > 7 && fraction >= 0.7)) {
306 List<Exprent> lstRet = new ArrayList<>();
308 VarType arrayType = newExpr.getNewType().decreaseArrayDim();
309 ConstExprent defaultVal = ExprProcessor.getDefaultArrayValue(arrayType);
310 for (int j = 0; j < size; j++) {
311 lstRet.add(defaultVal.copy());
314 int dims = newExpr.getNewType().arrayDim;
315 for (Entry<Integer, Exprent> ent : mapInit.entrySet()) {
316 Exprent tempExpr = ent.getValue();
317 lstRet.set(ent.getKey(), tempExpr);
319 if (tempExpr.type == Exprent.EXPRENT_NEW) {
320 NewExprent tempNewExpr = (NewExprent)tempExpr;
321 if (dims > 1 && !tempNewExpr.getLstArrayElements().isEmpty()) {
322 tempNewExpr.setDirectArrayInit(true);
327 newExpr.setLstArrayElements(lstRet);
329 return mapInit.size();
338 private static boolean isTrivialStackAssignment(Exprent first) {
339 if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
340 AssignmentExprent asf = (AssignmentExprent)first;
342 if (asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) {
343 VarExprent left = (VarExprent)asf.getLeft();
344 VarExprent right = (VarExprent)asf.getRight();
345 return left.getIndex() == right.getIndex() && left.isStack() && right.isStack();
352 private static boolean isStackAssignment2(Exprent first, Exprent second) { // e.g. 1.4-style class invocation
353 if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {
354 AssignmentExprent asf = (AssignmentExprent)first;
355 AssignmentExprent ass = (AssignmentExprent)second;
357 if (asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR &&
358 asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) {
359 if (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) {
360 asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight(), ass.bytecode));
369 private static boolean isStackAssignment(Exprent first, Exprent second) {
370 if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {
371 AssignmentExprent asf = (AssignmentExprent)first;
372 AssignmentExprent ass = (AssignmentExprent)second;
375 if (asf.getRight().equals(ass.getRight())) {
376 if ((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) &&
377 (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) {
379 if (!ass.getLeft().containsExprent(asf.getLeft())) {
385 if (asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) {
386 asf = (AssignmentExprent)asf.getRight();
397 private static Exprent isPPIorMMI(Exprent first) {
398 if (first.type == Exprent.EXPRENT_ASSIGNMENT) {
399 AssignmentExprent as = (AssignmentExprent)first;
401 if (as.getRight().type == Exprent.EXPRENT_FUNCTION) {
402 FunctionExprent func = (FunctionExprent)as.getRight();
404 if (func.getFuncType() == FunctionExprent.FUNCTION_ADD || func.getFuncType() == FunctionExprent.FUNCTION_SUB) {
405 Exprent econd = func.getLstOperands().get(0);
406 Exprent econst = func.getLstOperands().get(1);
408 if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST &&
409 func.getFuncType() == FunctionExprent.FUNCTION_ADD) {
411 econst = func.getLstOperands().get(0);
414 if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {
415 Exprent left = as.getLeft();
417 if (left.type != Exprent.EXPRENT_VAR && left.equals(econd)) {
418 int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI;
419 FunctionExprent ret = new FunctionExprent(type, econd, func.bytecode);
420 ret.setImplicitType(VarType.VARTYPE_INT);
431 private static boolean isIPPorIMM(Exprent first, Exprent second) {
432 if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) {
433 AssignmentExprent as = (AssignmentExprent)first;
434 FunctionExprent in = (FunctionExprent)second;
436 if ((in.getFuncType() == FunctionExprent.FUNCTION_MMI || in.getFuncType() == FunctionExprent.FUNCTION_PPI) &&
437 in.getLstOperands().get(0).equals(as.getRight())) {
439 if (in.getFuncType() == FunctionExprent.FUNCTION_MMI) {
440 in.setFuncType(FunctionExprent.FUNCTION_IMM);
443 in.setFuncType(FunctionExprent.FUNCTION_IPP);
454 private static boolean isIPPorIMM2(Exprent first, Exprent second) {
455 if (first.type != Exprent.EXPRENT_ASSIGNMENT || second.type != Exprent.EXPRENT_ASSIGNMENT) {
459 AssignmentExprent af = (AssignmentExprent)first;
460 AssignmentExprent as = (AssignmentExprent)second;
462 if (as.getRight().type != Exprent.EXPRENT_FUNCTION) {
466 FunctionExprent func = (FunctionExprent)as.getRight();
468 if (func.getFuncType() != FunctionExprent.FUNCTION_ADD && func.getFuncType() != FunctionExprent.FUNCTION_SUB) {
472 Exprent econd = func.getLstOperands().get(0);
473 Exprent econst = func.getLstOperands().get(1);
475 if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && func.getFuncType() == FunctionExprent.FUNCTION_ADD) {
477 econst = func.getLstOperands().get(0);
480 if (econst.type == Exprent.EXPRENT_CONST &&
481 ((ConstExprent)econst).hasValueOne() &&
482 af.getLeft().equals(econd) &&
483 af.getRight().equals(as.getLeft()) &&
484 (af.getLeft().getExprentUse() & Exprent.MULTIPLE_USES) != 0) {
485 int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_IPP : FunctionExprent.FUNCTION_IMM;
487 FunctionExprent ret = new FunctionExprent(type, af.getRight(), func.bytecode);
488 ret.setImplicitType(VarType.VARTYPE_INT);
497 private static boolean isMonitorExit(Exprent first) {
498 if (first.type == Exprent.EXPRENT_MONITOR) {
499 MonitorExprent expr = (MonitorExprent)first;
500 return expr.getMonType() == MonitorExprent.MONITOR_EXIT &&
501 expr.getValue().type == Exprent.EXPRENT_VAR &&
502 !((VarExprent)expr.getValue()).isStack();
508 private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) {
509 if (first.type == Exprent.EXPRENT_INVOCATION) {
510 InvocationExprent invocation = (InvocationExprent)first;
512 if (!invocation.isStatic() && invocation.getInstance().type == Exprent.EXPRENT_VAR && invocation.getName().equals("getClass") &&
513 invocation.getStringDescriptor().equals("()Ljava/lang/Class;")) {
515 List<Exprent> lstExprents = second.getAllExprents();
516 lstExprents.add(second);
518 for (Exprent expr : lstExprents) {
519 if (expr.type == Exprent.EXPRENT_NEW) {
520 NewExprent newExpr = (NewExprent)expr;
521 if (newExpr.getConstructor() != null && !newExpr.getConstructor().getParameters().isEmpty() &&
522 newExpr.getConstructor().getParameters().get(0).equals(invocation.getInstance())) {
524 String classname = newExpr.getNewType().value;
525 ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);
526 if (node != null && node.type != ClassNode.CLASS_ROOT) {
538 // propagate (var = new X) forward to the <init> invocation
539 private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) {
540 Exprent current = list.get(index);
542 if (current.type == Exprent.EXPRENT_ASSIGNMENT) {
543 AssignmentExprent as = (AssignmentExprent)current;
545 if (as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) {
547 NewExprent newExpr = (NewExprent)as.getRight();
548 VarType newType = newExpr.getNewType();
549 VarVersionPair leftPair = new VarVersionPair((VarExprent)as.getLeft());
551 if (newType.type == CodeConstants.TYPE_OBJECT && newType.arrayDim == 0 && newExpr.getConstructor() == null) {
552 for (int i = index + 1; i < list.size(); i++) {
553 Exprent remote = list.get(i);
556 if (remote.type == Exprent.EXPRENT_INVOCATION) {
557 InvocationExprent in = (InvocationExprent)remote;
559 if (in.getFuncType() == InvocationExprent.TYPE_INIT &&
560 in.getInstance().type == Exprent.EXPRENT_VAR &&
561 as.getLeft().equals(in.getInstance())) {
562 newExpr.setConstructor(in);
563 in.setInstance(null);
565 list.set(i, as.copy());
571 // check for variable in use
572 Set<VarVersionPair> setVars = remote.getAllVariables();
573 if (setVars.contains(leftPair)) { // variable used somewhere in between -> exit, need a better reduced code
584 private static Exprent isLambda(Exprent exprent, StructClass cl) {
585 List<Exprent> lst = exprent.getAllExprents();
586 for (Exprent expr : lst) {
587 Exprent ret = isLambda(expr, cl);
589 exprent.replaceExprent(expr, ret);
593 if (exprent.type == Exprent.EXPRENT_INVOCATION) {
594 InvocationExprent in = (InvocationExprent)exprent;
596 if (in.getInvocationType() == InvocationExprent.INVOKE_DYNAMIC) {
597 String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix();
598 ClassNode lambda_class = DecompilerContext.getClassProcessor().getMapRootClasses().get(lambda_class_name);
600 if (lambda_class != null) { // real lambda class found, replace invocation with an anonymous class
601 NewExprent newExpr = new NewExprent(new VarType(lambda_class_name, true), null, 0, in.bytecode);
602 newExpr.setConstructor(in);
603 // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invocation
604 // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);)
605 // in this case instance will hold the corresponding object
615 private static Exprent isSimpleConstructorInvocation(Exprent exprent) {
616 List<Exprent> lst = exprent.getAllExprents();
617 for (Exprent expr : lst) {
618 Exprent ret = isSimpleConstructorInvocation(expr);
620 exprent.replaceExprent(expr, ret);
624 if (exprent.type == Exprent.EXPRENT_INVOCATION) {
625 InvocationExprent in = (InvocationExprent)exprent;
626 if (in.getFuncType() == InvocationExprent.TYPE_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) {
627 NewExprent newExpr = (NewExprent)in.getInstance();
628 newExpr.setConstructor(in);
629 in.setInstance(null);
637 private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) {
638 if (stat.type == Statement.TYPE_IF && stat.getExprents() == null) {
639 IfStatement statement = (IfStatement)stat;
640 Exprent ifHeadExpr = statement.getHeadexprent();
641 Set<Integer> ifHeadExprBytecode = (ifHeadExpr == null ? null : ifHeadExpr.bytecode);
643 if (statement.iftype == IfStatement.IFTYPE_IFELSE) {
644 Statement ifStatement = statement.getIfstat();
645 Statement elseStatement = statement.getElsestat();
647 if (ifStatement.getExprents() != null && ifStatement.getExprents().size() == 1 &&
648 elseStatement.getExprents() != null && elseStatement.getExprents().size() == 1 &&
649 ifStatement.getAllSuccessorEdges().size() == 1 && elseStatement.getAllSuccessorEdges().size() == 1 &&
650 ifStatement.getAllSuccessorEdges().get(0).getDestination() == elseStatement.getAllSuccessorEdges().get(0).getDestination()) {
651 Exprent ifExpr = ifStatement.getExprents().get(0);
652 Exprent elseExpr = elseStatement.getExprents().get(0);
654 if (ifExpr.type == Exprent.EXPRENT_ASSIGNMENT && elseExpr.type == Exprent.EXPRENT_ASSIGNMENT) {
655 AssignmentExprent ifAssign = (AssignmentExprent)ifExpr;
656 AssignmentExprent elseAssign = (AssignmentExprent)elseExpr;
658 if (ifAssign.getLeft().type == Exprent.EXPRENT_VAR && elseAssign.getLeft().type == Exprent.EXPRENT_VAR) {
659 VarExprent ifVar = (VarExprent)ifAssign.getLeft();
660 VarExprent elseVar = (VarExprent)elseAssign.getLeft();
662 if (ifVar.getIndex() == elseVar.getIndex() && ifVar.isStack()) { // ifVar.getIndex() >= VarExprent.STACK_BASE) {
663 boolean found = false;
665 for (Entry<VarVersionPair, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {
666 if (ent.getKey().var == ifVar.getIndex()) {
667 if (ent.getValue().contains(ifVar.getVersion()) && ent.getValue().contains(elseVar.getVersion())) {
675 List<Exprent> data = new ArrayList<>(statement.getFirst().getExprents());
677 List<Exprent> operands = Arrays.asList(statement.getHeadexprent().getCondition(), ifAssign.getRight(), elseAssign.getRight());
678 data.add(new AssignmentExprent(ifVar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, operands, ifHeadExprBytecode), ifHeadExprBytecode));
679 statement.setExprents(data);
681 if (statement.getAllSuccessorEdges().isEmpty()) {
682 StatEdge ifEdge = ifStatement.getAllSuccessorEdges().get(0);
683 StatEdge edge = new StatEdge(ifEdge.getType(), statement, ifEdge.getDestination());
685 statement.addSuccessor(edge);
686 if (ifEdge.closure != null) {
687 ifEdge.closure.addLabeledEdge(edge);
691 SequenceHelper.destroyAndFlattenStatement(statement);
698 else if (ifExpr.type == Exprent.EXPRENT_EXIT && elseExpr.type == Exprent.EXPRENT_EXIT) {
699 ExitExprent ifExit = (ExitExprent)ifExpr;
700 ExitExprent elseExit = (ExitExprent)elseExpr;
702 if (ifExit.getExitType() == elseExit.getExitType() && ifExit.getValue() != null && elseExit.getValue() != null &&
703 ifExit.getExitType() == ExitExprent.EXIT_RETURN) {
704 // throw is dangerous, because of implicit casting to a common superclass
705 // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work
706 if (ifExit.getExitType() == ExitExprent.EXIT_THROW &&
707 !ifExit.getValue().getExprType().equals(elseExit.getValue().getExprType())) { // note: getExprType unreliable at this point!
711 // avoid flattening to 'iff' if any of the branches is an 'iff' already
712 if (isIff(ifExit.getValue()) || isIff(elseExit.getValue())) {
716 List<Exprent> data = new ArrayList<>(statement.getFirst().getExprents());
718 data.add(new ExitExprent(ifExit.getExitType(), new FunctionExprent(FunctionExprent.FUNCTION_IIF,
720 statement.getHeadexprent().getCondition(),
722 elseExit.getValue()), ifHeadExprBytecode), ifExit.getRetType(), ifHeadExprBytecode));
723 statement.setExprents(data);
725 StatEdge retEdge = ifStatement.getAllSuccessorEdges().get(0);
726 Statement closure = retEdge.closure == statement ? statement.getParent() : retEdge.closure;
727 statement.addSuccessor(new StatEdge(StatEdge.TYPE_BREAK, statement, retEdge.getDestination(), closure));
729 SequenceHelper.destroyAndFlattenStatement(statement);
741 private static boolean isIff(Exprent exp) {
742 return exp.type == Exprent.EXPRENT_FUNCTION && ((FunctionExprent) exp).getFuncType() == FunctionExprent.FUNCTION_IIF;
745 private static boolean collapseInlinedClass14(Statement stat) {
746 boolean ret = class14Builder.match(stat);
748 String class_name = (String)class14Builder.getVariableValue("$classname$");
749 AssignmentExprent assignment = (AssignmentExprent)class14Builder.getVariableValue("$assignfield$");
750 FieldExprent fieldExpr = (FieldExprent)class14Builder.getVariableValue("$field$");
752 assignment.replaceExprent(assignment.getRight(), new ConstExprent(VarType.VARTYPE_CLASS, class_name, null));
754 List<Exprent> data = new ArrayList<>(stat.getFirst().getExprents());
756 stat.setExprents(data);
758 SequenceHelper.destroyAndFlattenStatement(stat);
760 ClassWrapper wrapper = (ClassWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_WRAPPER);
761 if (wrapper != null) {
762 wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldExpr.getName(), fieldExpr.getDescriptor().descriptorString));